Zmeika
простая аркадная игра
 Указатель Классы Пространства имен Файлы Функции Переменные Определения типов Перечисления Страницы
Game.hpp
См. документацию.
1 /*
2  * This file is part of AV Orchid.
3  * Copyright (c) 2013, Dmitri R. Kuvshinov <evetro.here@gmail.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
22 #pragma once
23 #include <algorithm>
24 #include <vector>
25 #include <utility>
26 #include <limits>
27 
28 #include "ViewModelBase.hpp"
29 
30 
32 
36 class Field
37 {
38 public:
40 
44  using Position = std::pair<Index, Index>;
45 
47 
53  Field(Index horz, Index vert)
54  : xsize_(horz), ysize_(vert), cells(horz * vert, CELL_EMPTY)
55  {}
56 
58  Index xsize() const
59  {
60  return xsize_;
61  }
62 
64  Index ysize() const
65  {
66  return ysize_;
67  }
68 
71  {
72  return cells.at(x + xsize_ * y);
73  }
74 
77  {
78  return cells.at(x + xsize_ * y);
79  }
80 
82  Cell operator[](const Position &p) const
83  {
84  return (*this)(p.first, p.second);
85  }
86 
89  {
90  return (*this)(p.first, p.second);
91  }
92 
93 
95 
100  template <class Visitor>
101  void scan(Visitor visitor) const
102  {
103  visitor (0, 0, cells[0]);
104  for (Index x = 0, i = 1, sz = cells.size(); i < sz; ++i)
105  {
106  if (++x == xsize_)
107  {
108  visitor(1 - xsize_, 1, cells[i]);
109  x = 0;
110  }
111  else
112  {
113  visitor(1, 0, cells[i]);
114  }
115  }
116  }
117 
118 private:
119  Index xsize_, ysize_; // размеры поля
120  std::vector<Cell> cells; // ячейки поля
121 };
122 
123 
125 class Snake
126 {
127 public:
130 
132 
136  explicit Snake(Field &field, Index snake_len = 5)
137  : field(field)
138  {
139  create_snake(snake_len);
140  }
141 
143 
146  void reset(Index snake_len = 0)
147  {
148  for (const auto &p : positions)
149  field[p] = CELL_EMPTY;
150  create_snake(snake_len);
151  }
152 
154  void up()
155  {
156  vx = 0;
157  vy = +1;
158  check_reverse();
159  }
160 
162  void down()
163  {
164  vx = 0;
165  vy = -1;
166  check_reverse();
167  }
168 
170  void left()
171  {
172  vx = -1;
173  vy = 0;
174  check_reverse();
175  }
176 
178  void right()
179  {
180  vx = +1;
181  vy = 0;
182  check_reverse();
183  }
184 
186 
190  {
191  const Position new_head = moved(positions.front());
192  if (field.xsize() <= new_head.first || field.ysize() <= new_head.second)
193  return CELL_SNAKE;
194  return warp_head(new_head);
195  }
196 
198 
201  Cell warp_head(const Position &new_head)
202  {
203  switch (const Cell next_cell = field[new_head])
204  {
205  case CELL_SNAKE:
206  return CELL_SNAKE;
207 
208  case CELL_EMPTY:
209  field[positions.back()] = CELL_EMPTY;
210  positions.pop_back();
211  // fall-through
212  default:
213  field[new_head] = CELL_SNAKE;
214  positions.insert(positions.begin(), new_head);
215  return next_cell;
216  }
217  }
218 
220  Index length() const { return positions.size(); }
221 
222 private:
223 
224  using Positions = std::vector<Position>;
226  Positions positions;
227  Index vx, vy; // скорость по x, по y
228  Field &field; // привязка к полю
229 
231  void create_snake(Index snake_len)
232  {
233  vx = 0;
234  vy = 1;
235  positions.resize(snake_len);
236  for (Index i = 0; i < snake_len; ++i)
237  {
238  const Position block(snake_len - i - 1, 0);
239  positions[i] = block;
240  field[block] = CELL_SNAKE;
241  }
242  }
243 
245  Position moved(const Position &p) const
246  {
247  return Position(p.first + vx, p.second + vy);
248  }
249 
252  {
253  if (1 < positions.size() && moved(positions.front()) == positions[1])
254  {
255  std::reverse(positions.begin(), positions.end());
256  // даже после обращения порядка, "голова" может двигаться в "шею"
257  if (moved(positions.front()) == positions[1])
258  vx = positions.front().first - positions[1].first ,
259  vy = positions.front().second - positions[1].second;
260  }
261  }
262 };
263 
264 
266 struct Level
267 {
269  max_length;
270 
271  double speed;
272 
274  penalty;
275 
276  Level()
277  : start_length(4),
278  max_length(10),
279  speed(4.0),
280  max_movement(1200),
281  penalty(300)
282  {}
283 };
284 
285 
288 {
289 public:
290  Zmeika_game();
291 
294 
296  Index get_level() const { return level; }
298  Score get_score() const { return score; }
300  Score get_score_max() const { return score_max; }
301 
303  void up() { snake.up(); }
305  void down() { snake.down(); }
307  void left() { snake.left(); }
309  void right() { snake.right(); }
310 
312  void start()
313  {
314  read_levels();
315  new_rabbit(CELL_RABBIT);
316  select_level(1);
317  score = score_max = 0;
318  }
319 
321 
324  bool warp_snake(const Field::Position &new_head)
325  {
326  return moved_into(snake.warp_head(new_head));
327  }
328 
329 protected:
330 
332  double get_time_step() const { return time_step; }
334  bool do_step()
335  {
336  return moved_into(snake.step());
337  }
338 
339 private:
340 
342  void read_levels();
343 
345  void new_rabbit(Cell rabbit_type);
347  void select_level(Index next_level);
348 
350 
353  bool moved_into(const Cell);
354 
356  void add_score(Score amount);
358  void sub_score(Score amount);
359 
366 
370  Score move_score[CELL_TYPES];
372  double time_step;
374  std::vector<Level> levels;
379  // Осторожно: конструктор принимает field, поэтому snake дб расположена
380  // в классе ниже, чем field, чтобы конструктор field отработал до конструктора snake.
381 };