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 
24 #include <algorithm>
25 #include <vector>
26 #include <utility>
27 #include <limits>
28 
29 #include "ViewModelBase.hpp"
30 
31 
33 class Field
34 {
35 public:
37 
41  typedef std::pair<Index, Index> Position;
42 
44  Field(Index horz, Index vert)
45  : xsize_(horz), ysize_(vert), cells(horz * vert, Cell_Empty)
46  {}
47 
49  Index xsize() const
50  {
51  return xsize_;
52  }
53 
55  Index ysize() const
56  {
57  return ysize_;
58  }
59 
61  Cell operator()(Index x, Index y) const
62  {
63  return cells.at(x + xsize_ * y);
64  }
65 
68  {
69  return cells.at(x + xsize_ * y);
70  }
71 
73  Cell operator[](const Position &p) const
74  {
75  return (*this)(p.first, p.second);
76  }
77 
80  {
81  return (*this)(p.first, p.second);
82  }
83 
84 
86 
90  template <class Visitor>
91  void scan(Visitor visitor) const
92  {
93  visitor (0, 0, cells[0]);
94  for (Index x = 0, i = 1, sz = cells.size(); i < sz; ++i)
95  {
96  if (++x == xsize_)
97  visitor (1 - xsize_, 1, cells[i]), x = 0;
98  else
99  visitor (1, 0, cells[i]);
100  }
101  }
102 
103 private:
104  Index xsize_, ysize_; // размеры поля
105  std::vector<Cell> cells; // ячейки поля
106 };
107 
108 
110 class Snake
111 {
112 public:
113  typedef Field::Position Position;
114 
116  explicit Snake(Field &field, Index snake_len = 5)
117  {
118  createSnake (field, snake_len);
119  }
120 
122  void reset(Field &field, Index snake_len)
123  {
124  for (const auto &p : positions)
125  field[p] = Cell_Empty;
126  createSnake (field, snake_len);
127  }
128 
130  void up()
131  {
132  vx = 0;
133  vy = +1;
134  check_reverse ();
135  }
136 
138  void down()
139  {
140  vx = 0;
141  vy = -1;
142  check_reverse ();
143  }
144 
146  void left()
147  {
148  vx = -1;
149  vy = 0;
150  check_reverse ();
151  }
152 
154  void right()
155  {
156  vx = +1;
157  vy = 0;
158  check_reverse ();
159  }
160 
162 
165  Cell step(Field &field)
166  {
167  const Position new_head = moved(positions.front());
168  if (field.xsize() <= new_head.first || field.ysize() <= new_head.second)
169  return Cell_Snake;
170  return warpHead(field, new_head);
171  }
172 
174 
177  Cell warpHead(Field &field, const Position &new_head)
178  {
179  switch (const Cell next_cell = field[new_head])
180  {
181  case Cell_Snake:
182  return Cell_Snake;
183 
184  case Cell_Empty:
185  field[positions.back()] = Cell_Empty;
186  positions.pop_back ();
187  // fall-through
188  default:
189  field[new_head] = Cell_Snake;
190  positions.insert (positions.begin(), new_head);
191  return next_cell;
192  }
193  }
194 
196  Index length() const { return positions.size(); }
197 
198 private:
199 
200  typedef std::vector<Position> Positions;
202  Positions positions;
203  Index vx, vy;
204 
206  void createSnake(Field &field, Index snake_len)
207  {
208  vx = 0;
209  vy = 1;
210  positions.resize (snake_len);
211  for (Index i = 0; i < snake_len; ++i)
212  {
213  const Position block(snake_len - i - 1, 0);
214  positions[i] = block;
215  field[block] = Cell_Snake;
216  }
217  }
218 
220  Position moved(const Position &p) const
221  {
222  return Position(p.first + vx, p.second + vy);
223  }
224 
227  {
228  if (1 < positions.size() && moved(positions.front()) == positions[1])
229  {
230  std::reverse (positions.begin(), positions.end());
231  // даже после обращения порядка, "голова" может двигаться в "шею"
232  if (moved(positions.front()) == positions[1])
233  vx = positions.front().first - positions[1].first ,
234  vy = positions.front().second - positions[1].second;
235  }
236  }
237 };
238 
239 
241 struct Level
242 {
244  max_length;
245  double speed;
247  penalty;
248 
249  Level()
250  : start_length(4),
251  max_length(10),
252  speed(4.0),
253  max_movement(1200),
254  penalty(300)
255  {}
256 };
257 
258 
261 {
262  // этот класс введён потому, что мне претит на каждом кадре преобразовывать
263  // число в текст, пусть это и не окажет сколько-нибудь заметного влияния на скорость
264 public:
265  virtual ~NumberPresenter() {} // actually not needed here
266  /*implicit*/ NumberPresenter(Score n = 0);
267 
269  operator Score() const { return number;}
271  const std::string& str() const { return repr; }
272 
273  NumberPresenter& operator+=(Score inc);
274  NumberPresenter& operator++() { return (*this) += 1; }
275 
276 protected:
278  virtual void update(Score n);
279 
280 private:
281  Score number;
282  std::string repr;
283 };
284 
285 
287 
291 {
292 public:
294  : NumberPresenter(n), max_val(n) {}
295 
296  NumberPresenterDecrementable& operator-=(Score inc);
297  NumberPresenterDecrementable& operator--() { return (*this) -= 1; }
299  Score getMax() const { return max_val; }
300 
301 protected:
302  void update(Score n) override;
303 
304 private:
305  Score max_val;
306 };
307 
308 
311 {
312 public:
313  ZmeikaGame();
314 
316  double last_time;
317 
320 
323 
326 
328  void up() { snake.up (); }
330  void down() { snake.down (); }
332  void left() { snake.left (); }
334  void right() { snake.right (); }
335 
337 
343  bool step(double t);
344 
346  void start()
347  {
348  readLevels ();
349  newRabbit (Cell_Rabbit);
350  selectLevel (1);
351  score = 0;
352  }
353 
355 
358  bool warpSnake(const Field::Position &new_head)
359  {
360  return movedInto(snake.warpHead(field, new_head));
361  }
362 
363 private:
364 
366  void readLevels();
367 
369  void newRabbit(Cell rabbitType);
371  void selectLevel(Index next_level);
372 
374 
377  bool movedInto(const Cell);
378 
382  Score move_score[Cell_Types];
384  double time_step;
386  std::vector<Level> levels;
391 };