HOG2
SnakeBird.cpp
Go to the documentation of this file.
1 //
2 // SnakeBird.cpp
3 // hog2 glut
4 //
5 // Created by Nathan Sturtevant on 1/26/20.
6 // Copyright © 2020 University of Denver. All rights reserved.
7 //
8 
9 #include "SnakeBird.h"
10 #include <fstream>
11 #include <cmath>
12 #include <algorithm>
13 
14 namespace SnakeBird {
15 
22 :width(width), height(height), editing(false)
23 {
24  assert(width*height <= 512);
25  assert(width >= 5 && height >= 5);
26  portal1Loc = portal2Loc = -1;
27  exitLoc = -1;
28  std::fill_n(&world[0], 512, kEmpty);
29  for (int x = 0; x < width; x++)
30  world[GetIndex(x, height-1)] = kSpikes;
31 }
32 
34 void SnakeBird::Reset()
35 {
36  width = 20;
37  height = 16;
38  portal1Loc = portal2Loc = -1;
39  exitLoc = -1;
40  std::fill_n(&world[0], 512, kEmpty);
41  for (int x = 0; x < width; x++)
42  world[GetIndex(x, height-1)] = kSpikes;
43  startState.Reset();
44  fruit.clear();
45  for (int x = 0; x < objects.size(); x++)
46  objects[x].clear();
47 }
48 
49 void SnakeBird::BiggerMapHeight()
50 {
51  if (width*(height+1) <= 512) // map cannot have more than 512 things in it
52  {
53  std::array<SnakeBirdWorldObject, 512> worldCopy;
54  worldCopy = world;
55  for (int x = 0; x < (width*height); x++)
56  world[x] = kEmpty;
57 
58  for (int snake = 0; startState.GetNumSnakes()-1 >= snake; snake++) // for snake
59  {
60  int snakeHead = startState.GetSnakeHeadLoc(snake);
61  std::vector<snakeDir> snakeBody;
62  for (int t = 0; t <= startState.GetSnakeLength(snake)-2; t++)
63  {
64  snakeBody.push_back(startState.GetSnakeDir(snake, t));
65  }
66 
67  for (int i = 0; i <= height+1; i++)
68  {
69  if (snakeHead/(height) >= i && snakeHead/(height) < i+1)
70  {
71  startState.SetSnakeHeadLoc(snake, snakeHead+i+1);
72  startState.SetSnakeLength(snake, 1);
73  break;
74  }
75  }
76  if (snakeBody.size() >= 1)
77  {
78  startState.SetSnakeLength(snake, snakeBody.size()+1);
79  for (int x = 0; x < snakeBody.size(); x++)
80  startState.SetSnakeDir(snake, x, snakeBody[x]);
81  }
82  }
83 
84  height++;
85 
86  //TODO:: 1. make the blocks(ground) print accurately after y = 15 or 18
87  //TODO:: 2. figure out the offset for blocks/make them reprint accruetly
88 
89  for (int x = 0; x <= worldCopy.size()-1; x++) //put ground back in
90  {
91  if (worldCopy[x] != kEmpty)
92  {
93  for (int i = 0; i <= height; i++)
94  {
95  if (x/(height-1) >= i && x/(height-1) < i+1)
96  {
97  if (i >= 18)
98  {
99  world[x+i+1] = worldCopy[x];
100  break;
101  }
102  else if (i <= 17)
103  {
104  world[x+i+1] = worldCopy[x];
105  break;
106  }
107  }
108  }
109  }
110  }
111 
112  if (portal1Loc != -1) //put portals back in
113  {
114  for (int i = 0; i <= height; i++)
115  {
116  if (portal1Loc/(height-1) >= i && portal1Loc/(height-1) < i+1)
117  {
118  portal1Loc = portal1Loc+i+1;
119  break;
120  }
121  }
122  }
123 
124  if (portal2Loc != -1) //put portals back in
125  {
126  for (int i = 0; i <= height; i++)
127  {
128  if (portal2Loc/(height-1) >= i && portal2Loc/(height-1) < i+1)
129  {
130  portal2Loc = portal2Loc+i+1;
131  break;
132  }
133  }
134  }
135 
136  if (exitLoc != -1) //put exit back in
137  {
138  for (int i = 1; i <= width; i++)
139  {
140  if (exitLoc/(height-1) >= i && exitLoc/(height-1) < i+1)
141  {
142  exitLoc = exitLoc+i+1;
143  break;
144  }
145  }
146  }
147  if (fruit.size() >= 1)
148  {
149  for (int x = 0; x <= fruit.size()-1; x++) //put grounds back in
150  {
151  for (int i = 1; i <= height; i++)
152  {
153  if (fruit[x]/(height-1) >= i && fruit[x]/(height-1) < i+1)
154  {
155  fruit[x] = fruit[x]+i+1;
156  break;
157  }
158  }
159  }
160  }
161 
162  for (int which = 0; which <= 3; which++)
163  {
164  if (objects[which].size() >= 1)
165  {
166  for (int vector = 0; vector <= objects[which].size()-1; vector++)
167  {
168  for (int i = 1; i <= height; i++)
169  {
170  if (objects[which][vector]/(height-1) >= i && objects[which][vector]/(height-1) < i+1) //(portal1Loc/(height-1) >= i && portal1Loc/(height-1) < i+1)
171  {
172  objects[which][vector] = objects[which][vector]+i+1; //fruit[x] = fruit[x]+i+1;
173  break;
174  }
175  }
176  }
177  }
178 
179 // int newX = 0, newY=0;
180 // int xOffset = 0, yOffset = 0;
181 // if (objects[which].size() > 0) // other objects - get their offset base
182 // {
183 // xOffset = GetX(startState.GetObjectLocation(which));
184 // yOffset = GetY(startState.GetObjectLocation(which));
185 // }
186 //
187 // // Get new base location (minx/y)
188 // for (int i = 0; i < objects[which].size(); i++)
189 // {
190 // newX = std::min(newX, GetX(objects[which][i])+xOffset);
191 // newY = std::min(newY, GetY(objects[which][i])+yOffset);
192 // }
193 // startState.SetObjectLocation(which, GetIndex(newX, newY));
194 // for (int i = 0; i < objects[which].size(); i++)
195 // {
196 // // reset locations based on new base location
197 // objects[which][i] = GetIndex(GetX(objects[which][i])+xOffset - newX,
198 // GetY(objects[which][i])+yOffset - newY);
199 // }
200 // // add new piece of new object
201 // objects[which].push_back(GetIndex(x-newX, y-newY));
202  }
203 
204  }
205 }
206 
207 void SnakeBird::BiggerMapWidth()
208 {
209  if ((width+1)*height <= 512)
210  width++;
211 }
212 
213 
214 void SnakeBird::SmallerMapHeight()
215 {
216  if ((height-1) >= 5)
217  {
218  std::array<SnakeBirdWorldObject, 512> worldCopy;
219  worldCopy = world;
220  for (int x = 0; x < (width*height); x++)
221  world[x] = kEmpty;
222 
223  //TODO:: 1. make the snakes work (reprinting them when they go off the screen)
224  //TODO:: 2. figure out the offset for blocks after y = 16 or around there
225  //TODO:: 2. make the blocks(ground) reprint accurately
226 
227  for (int snake = 0; startState.GetNumSnakes()-1 >= snake; snake++) // for snake
228  {
229  int snakeHead = startState.GetSnakeHeadLoc(snake);
230  int endofSnakeX = GetX(snakeHead);
231  int endofSnakeY = GetY(snakeHead);
232  std::vector<snakeDir> snakeBody;
233  std::vector<int> snakeBodyCoordinates;
234  for (int t = 0; t <= startState.GetSnakeLength(snake)-2; t++)
235  {
236  if (startState.GetSnakeDir(snake, t) == kRight)
237  {
238  endofSnakeX++;
239  }
240  else if (startState.GetSnakeDir(snake, t) == kLeft)
241  {
242  endofSnakeX--;
243  }
244  else if (startState.GetSnakeDir(snake, t) == kDown)
245  {
246  endofSnakeY++;
247  }
248  else if (startState.GetSnakeDir(snake, t) == kUp)
249  {
250  endofSnakeY--;
251  }
252  snakeBody.push_back(startState.GetSnakeDir(snake, t));
253  }
254  if (startState.GetSnakeHeadLoc(snake)%height == 0)
255  {
256  for (int i = 0; i <= height-1; i++)
257  {
258  if ((snakeHead-1)/(height) >= i && (snakeHead-1)/(height) < i+1)
259  {
260  startState.SetSnakeHeadLoc(snake, snakeHead-i-2); //GetIndex(GetX(snakeHead), GetY(snakeHead)-1)-i-1
261  startState.SetSnakeLength(snake, snakeBody.size()+1);
262  if (snakeBody.size() >= 1)
263  {
264  for (int x = 0; x < snakeBody.size(); x++)
265  startState.SetSnakeDir(snake, x, snakeBody[x]);
266  }
267  }
268  }
269  }
270  else
271  {
272  for (int i = 0; i <= height-1; i++)
273  {
274  if ((snakeHead-1)/(height) >= i && (snakeHead-1)/(height) < i+1)
275  {
276  startState.SetSnakeHeadLoc(snake, snakeHead-i-1);
277  startState.SetSnakeLength(snake, 1);
278  break;
279  }
280  }
281  if (snakeBody.size() >= 1)
282  {
283  startState.SetSnakeLength(snake, snakeBody.size()+1);
284  for (int x = 0; x < snakeBody.size(); x++)
285  startState.SetSnakeDir(snake, x, snakeBody[x]);
286  }
287  }
288  }
289 
290  height--;
291 
292  for (int x = 0; x <= worldCopy.size()-1; x++) //put ground back in
293  {
294  if (worldCopy[x] != kEmpty)
295  {
296  for (int i = 0; i <= width; i++)
297  {
298  if ((x-1)/(height+1) >= i && (x-1)/(height+1) < i+1 && x%(height+1) != 0)
299  {
300  if (i >= 15)
301  {
302  world[x-i-1-15] = worldCopy[x];
303  break;
304  }
305  else if (i <= 14)
306  {
307  world[x-i-1] = worldCopy[x];
308  break;
309  }
310  }
311  }
312  }
313  }
314 
315  if (portal1Loc != -1) //put portals back in
316  {
317  for (int i = 0; i <= height; i++)
318  {
319  if (portal1Loc%(height+1) == 0)
320  {
321  portal1Loc = -1;
322  break;
323  }
324  if ((portal1Loc-1)/(height+1) >= i && (portal1Loc-1)/(height+1) < i+1)
325  {
326  portal1Loc = portal1Loc-i-1;
327  break;
328  }
329  }
330  }
331 
332  if (portal2Loc != -1) //put portals back in
333  {
334  for (int i = 0; i <= height; i++)
335  {
336  if (portal2Loc%(height+1) == 0)
337  {
338  portal2Loc = -1;
339  break;
340  }
341  if ((portal2Loc-1)/(height+1) >= i && (portal2Loc-1)/(height+1) < i+1)
342  {
343  portal2Loc = portal2Loc-i-1;
344  break;
345  }
346  }
347  }
348 
349  if (exitLoc != -1) //put exit back in
350  {
351  for (int i = 1; i <= width; i++)
352  {
353  if (exitLoc%(height+1) == 0)
354  {
355  exitLoc = -1;
356  break;
357  }
358  if ((exitLoc-1)/(height+1) >= i && (exitLoc-1)/(height+1) < i+1)
359  {
360  exitLoc = exitLoc-i-1;
361  break;
362  }
363  }
364  }
365 
366  if (fruit.size() >= 1)
367  {
368  for (int x = 0; x <= fruit.size()-1; x++) //put grounds back in
369  {
370  for (int i = 1; i <= height; i++)
371  {
372  if (fruit[x]%(height+1) == 0)
373  {
374  std::vector<int>::iterator tor = std::find(fruit.begin(), fruit.end(), GetIndex(x, 0)); //comeback for the y value
375  if (tor != fruit.end())
376  {
377  fruit.erase(tor);
378  }
379  break;
380  }
381  if ((fruit[x]-1)/(height+1) >= i && (fruit[x]-1)/(height+1) < i+1)
382  {
383  fruit[x] = fruit[x]-i-1;
384  break;
385  }
386  }
387  }
388  }
389 
390  for (int which = 0; which <= 3; which++)
391  {
392  if (objects[which].size() >= 1)
393  {
394  for (int vector = 0; vector <= objects[which].size()-1; vector++)
395  {
396  for (int i = 1; i <= height; i++)
397  {
398  if ((objects[which][vector]+1)/(height+1) >= i && (objects[which][vector]-1)/(height+1) < i+1 && objects[which][vector]%(height+1) != 0)
399  {
400  objects[which][vector] = objects[which][vector]-i-1;
401  break;
402  }
403  }
404  }
405  }
406  }
407 
408  }
409 }
410 
411 void SnakeBird::SmallerMapWidth()
412 {
413  if ((width-1) >= 5)
414  {
415  //TODO:: 1. make the snakes work (reprinting them when they go off the screen)
416 
417  for (int snake = 0; startState.GetNumSnakes()-1 >= snake; snake++) // for snake
418  {
419  int snakeHead = startState.GetSnakeHeadLoc(snake);
420  int endofSnakeX = GetX(snakeHead);
421  int endofSnakeY = GetY(snakeHead);
422  std::vector<snakeDir> snakeBody;
423  for (int t = 0; t <= startState.GetSnakeLength(snake)-2; t++)
424  {
425  if (startState.GetSnakeDir(snake, t) == kRight)
426  {
427  endofSnakeX++;
428  }
429  else if (startState.GetSnakeDir(snake, t) == kLeft)
430  {
431  endofSnakeX--;
432  }
433  else if (startState.GetSnakeDir(snake, t) == kDown)
434  {
435  endofSnakeY++;
436  }
437  else if (startState.GetSnakeDir(snake, t) == kUp)
438  {
439  endofSnakeY--;
440  }
441 
442  if (endofSnakeX == width-1)
443  {
444  startState.SetSnakeLength(snake, t);
445  break;
446  }
447  snakeBody.push_back(startState.GetSnakeDir(snake, t));
448  }
449  if (GetX(startState.GetSnakeHeadLoc(snake)) == width-1)
450  {
451  startState.SetSnakeHeadLoc(snake, GetIndex(GetX(snakeHead)-1, GetY(snakeHead)));
452  startState.SetSnakeLength(snake, snakeBody.size()+1);
453  if (snakeBody.size() >= 1)
454  {
455  for (int x = 0; x < snakeBody.size(); x++)
456  startState.SetSnakeDir(snake, x, snakeBody[x]);
457  }
458  }
459  }
460  width--;
461  }
462 }
463 
468 void SnakeBird::BeginEditing()
469 {
470  editing = true;
471 }
472 
476 void SnakeBird::EndEditing()
477 {
478  editing = false;
479  for (int x = 0; x < 4; x++)
480  {
481  bool connected = false;
482 
483  for (int u = 0; u < objects[x].size(); u++)
484  {
485  for (int v = 0; v < objects[x].size(); v++)
486  {
487  if (Distance(objects[x][u], objects[x][v]) == 1)
488  connected = true;
489  }
490  }
491  objectFullyConnected[x] = connected;
492  }
493 }
494 
495 
501 SnakeBirdState SnakeBird::GetStart() const
502 {
503  return startState;
504 }
505 
509 void SnakeBird::SetStart(const SnakeBirdState &start)
510 {
511  startState = start;
512 }
513 
514 
519 void SnakeBird::AddSnake(int x, int y, const std::vector<snakeDir> &body)
520 {
521  int count = startState.GetNumSnakes();
522  startState.SetNumSnakes(count+1);
523  startState.SetSnakeHeadLoc(count, GetIndex(x, y));
524  startState.SetSnakeLength(count, body.size()+1);
525  for (int x = 0; x < body.size(); x++)
526  startState.SetSnakeDir(count, x, body[x]);
527 }
528 
529 void SnakeBird::AddSnakeHead(int x, int y, int whichSnake)
530 {
531  if (GetIndex(x, y) >= 0 && GetIndex(x, y) < width*height && y <= height-2 && x <= width-1 && GetIndex(x, y) != startState.GetSnakeHeadLoc(whichSnake))
532  {
533  if (startState.GetNumSnakes() == whichSnake)
534  startState.SetNumSnakes(whichSnake+1);
535  startState.SetSnakeHeadLoc(whichSnake, GetIndex(x, y));
536  startState.SetSnakeLength(whichSnake, 1);
537  }
538 }
539 
540 snakeDir SnakeBird::GetAddingDirection(int x, int y, int endX, int endY)
541 {
542  if ((x-1 == endX) && (y == endY))
543  {
544  return kRight;
545  }
546  else if ((x+1 == endX) && (y == endY))
547  {
548  return kLeft;
549  }
550  else if ((y-1 == endY) && (x == endX))
551  {
552  return kDown;
553  }
554  else if ((y+1 == endY) && (x == endX))
555  {
556  return kUp;
557  }
558  return kNoDirection;
559 }
560 
561 void SnakeBird::AddSnakeBody(int x, int y, int whichSnake)
562 {
563  if (GetIndex(x, y) >= 0 && GetIndex(x, y) < width*height && y <= height-2 && x <= width-1)
564  {
565  int endofSnakeX = GetX(startState.GetSnakeHeadLoc(whichSnake)); // these track where the coordinates of the snake 'butt'(end) are
566  int endofSnakeY = GetY(startState.GetSnakeHeadLoc(whichSnake));
567  if (startState.GetSnakeLength(whichSnake) >= 2 && startState.GetSnakeLength(whichSnake) <= 30)
568  {
569  for (int t = 0; t <= startState.GetSnakeLength(whichSnake)-2; t++) //Go through the snake segments and find where the end of the snake is
570  {
571  if (startState.GetSnakeDir(whichSnake, t) == kRight)
572  {
573  endofSnakeX++;
574  }
575  else if (startState.GetSnakeDir(whichSnake, t) == kLeft)
576  {
577  endofSnakeX--;
578  }
579  else if (startState.GetSnakeDir(whichSnake, t) == kDown)
580  {
581  endofSnakeY++;
582  }
583  else if (startState.GetSnakeDir(whichSnake, t) == kUp)
584  {
585  endofSnakeY--;
586  }
587  }
588  }
589  if (GetAddingDirection(x, y, endofSnakeX, endofSnakeY) != kNoDirection)
590  {
591  if (whichSnake == 0 && startState.GetNumSnakes() == 2)
592  {
593  startState.MakeSnakeLonger(whichSnake, GetAddingDirection(x, y, endofSnakeX, endofSnakeY));
594  }
595  else
596  {
597  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)+1);
598  startState.SetSnakeDir(whichSnake, startState.GetSnakeLength(whichSnake)-2, GetAddingDirection(x, y, endofSnakeX, endofSnakeY));
599  //TODO:: There's an issue here with the program in the adding direction after 4 segments
600  }
601  }
602  }
603 }
604 
605 
606 void SnakeBird::RemoveSnake(int x, int y, int o, int whichSnake)
607 {
608  int endofSnakeX = GetX(startState.GetSnakeHeadLoc(whichSnake));
609  int endofSnakeY = GetY(startState.GetSnakeHeadLoc(whichSnake));
610  if (startState.GetSnakeLength(whichSnake) >= 2 && startState.GetSnakeLength(whichSnake) <= 30)
611  {
612  for (int t = 0; t <= startState.GetSnakeLength(whichSnake)-3; t++) //Go through the snake segments and find where the end of the snake is
613  {
614  if (startState.GetSnakeDir(whichSnake, t) == kRight)
615  {
616  endofSnakeX++;
617  }
618  else if (startState.GetSnakeDir(whichSnake, t) == kLeft)
619  {
620  endofSnakeX--;
621  }
622  else if (startState.GetSnakeDir(whichSnake, t) == kDown)
623  {
624  endofSnakeY++;
625  }
626  else if (startState.GetSnakeDir(whichSnake, t) == kUp)
627  {
628  endofSnakeY--;
629  }
630  }
631  }
632  if (whichSnake == 0)
633  {
634  if (o == 1) // if click
635  {
636  if (x == endofSnakeX && y == endofSnakeY && startState.GetSnakeLength(whichSnake) >= 3)
637  {
638  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)-1);
639  }
640  else if (GetIndex(x, y) == startState.GetSnakeHeadLoc(whichSnake) && startState.GetNumSnakes() >= 2 && startState.GetNumSnakes() == 2)
641  {
642  std::vector<snakeDir> snakeBody;
643  int snakeHead = startState.GetSnakeHeadLoc(1);
644  for (int t = 0; t <= startState.GetSnakeLength(1)-2; t++)
645  {
646  if (startState.GetSnakeDir(1, t) == kRight)
647  {
648  snakeBody.push_back(kRight);
649  }
650  else if (startState.GetSnakeDir(1, t) == kLeft)
651  {
652  snakeBody.push_back(kLeft);
653  }
654  else if (startState.GetSnakeDir(1, t) == kDown)
655  {
656  snakeBody.push_back(kDown);
657  }
658  else if (startState.GetSnakeDir(1, t) == kUp)
659  {
660  snakeBody.push_back(kUp);
661  }
662  }
663  startState.SetSnakeLength(whichSnake, 0);
664  startState.SetSnakeLength(whichSnake, snakeBody.size()+1);
665  startState.SetSnakeHeadLoc(whichSnake, snakeHead);
666  for (int b = 0; b <= snakeBody.size(); b++)
667  startState.SetSnakeDir(whichSnake, b, snakeBody[b]);
668  startState.SetNumSnakes(startState.GetNumSnakes()-1);
669  }
670  else
671  {
672  std::vector<int> snakeBody;
673  int snakeBodyCounterX = GetX(startState.GetSnakeHeadLoc(whichSnake));
674  int snakeBodyCounterY = GetY(startState.GetSnakeHeadLoc(whichSnake));
675  for (int t = 0; t <= startState.GetSnakeLength(whichSnake)-2; t++)
676  {
677  if (startState.GetSnakeDir(whichSnake, t) == kRight)
678  {
679  snakeBodyCounterX++;
680  snakeBody.push_back(GetIndex(snakeBodyCounterX+1, snakeBodyCounterY));
681  }
682  else if (startState.GetSnakeDir(whichSnake, t) == kLeft)
683  {
684  snakeBodyCounterX--;
685  snakeBody.push_back(GetIndex(snakeBodyCounterX-1, snakeBodyCounterY));
686  }
687  else if (startState.GetSnakeDir(whichSnake, t) == kDown)
688  {
689  snakeBodyCounterY++;
690  snakeBody.push_back(GetIndex(snakeBodyCounterX, snakeBodyCounterY+1));
691  }
692  else if (startState.GetSnakeDir(whichSnake, t) == kUp)
693  {
694  snakeBodyCounterY--;
695  snakeBody.push_back(GetIndex(snakeBodyCounterX, snakeBodyCounterY-1));
696  }
697  }
698 
699  for (int i = 0; i <= snakeBody.size()-1; i++)
700  {
701  if (GetIndex(x, y) == snakeBody[i])
702  {
703  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)-snakeBody.size()-i);
704  }
705  }
706  }
707  }
708  else if (o == 0) // if drag
709  {
710  if (x == endofSnakeX && y == endofSnakeY && startState.GetSnakeLength(whichSnake) >= 3)
711  {
712  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)-1);
713  }
714  else if (GetIndex(x, y) == startState.GetSnakeHeadLoc(whichSnake) && startState.GetSnakeLength(whichSnake) >= 2 && startState.GetNumSnakes() >= 2)
715  {
716  std::vector<snakeDir> snakeBody;
717  int snakeHead = startState.GetSnakeHeadLoc(whichSnake);
718  for (int t = 0; t <= startState.GetSnakeLength(whichSnake)-2; t++)
719  {
720  if (startState.GetSnakeDir(whichSnake, t) == kRight)
721  {
722  snakeBody.push_back(kRight);
723  }
724  else if (startState.GetSnakeDir(whichSnake, t) == kLeft)
725  {
726  snakeBody.push_back(kLeft);
727  }
728  else if (startState.GetSnakeDir(whichSnake, t) == kDown)
729  {
730  snakeBody.push_back(kDown);
731  }
732  else if (startState.GetSnakeDir(whichSnake, t) == kUp)
733  {
734  snakeBody.push_back(kUp);
735  }
736  }
737  startState.SetSnakeLength(whichSnake, 0);
738  startState.SetSnakeLength(whichSnake, snakeBody.size()+1);
739  startState.SetSnakeHeadLoc(whichSnake, snakeHead);
740  if (startState.GetSnakeLength(whichSnake) > 1)
741  {
742  for (int b = 0; b <= snakeBody.size(); b++)
743  startState.SetSnakeDir(whichSnake, b, snakeBody[b]);
744  }
745  startState.SetNumSnakes(startState.GetNumSnakes()-1);
746  }
747  }
748  }
749  if (whichSnake == 1)
750  {
751  if (o == 1) // if click
752  {
753  if (x == endofSnakeX && y == endofSnakeY)
754  {
755  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)-1);
756  }
757  else if (GetIndex(x, y) == startState.GetSnakeHeadLoc(whichSnake) && startState.GetSnakeLength(whichSnake) >= 2)
758  {
759  startState.SetNumSnakes(startState.GetNumSnakes()-1);
760  }
761  else
762  {
763  std::vector<int> snakeBody;
764  int snakeBodyCounterX = GetX(startState.GetSnakeHeadLoc(whichSnake));
765  int snakeBodyCounterY = GetY(startState.GetSnakeHeadLoc(whichSnake));
766  for (int t = 0; t <= startState.GetSnakeLength(whichSnake)-2; t++)
767  {
768  if (startState.GetSnakeDir(whichSnake, t) == kRight)
769  {
770  snakeBodyCounterX++;
771  snakeBody.push_back(GetIndex(snakeBodyCounterX+1, snakeBodyCounterY));
772  }
773  else if (startState.GetSnakeDir(whichSnake, t) == kLeft)
774  {
775  snakeBodyCounterX--;
776  snakeBody.push_back(GetIndex(snakeBodyCounterX-1, snakeBodyCounterY));
777  }
778  else if (startState.GetSnakeDir(whichSnake, t) == kDown)
779  {
780  snakeBodyCounterY++;
781  snakeBody.push_back(GetIndex(snakeBodyCounterX, snakeBodyCounterY+1));
782  }
783  else if (startState.GetSnakeDir(whichSnake, t) == kUp)
784  {
785  snakeBodyCounterY--;
786  snakeBody.push_back(GetIndex(snakeBodyCounterX, snakeBodyCounterY-1));
787  }
788  }
789 
790  for (int i = 0; i <= snakeBody.size()-1; i++)
791  {
792  if (GetIndex(x, y) == snakeBody[i])
793  {
794  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)-snakeBody.size()-i);
795  }
796  }
797  }
798  }
799  else if (o == 0) // if drag
800  {
801  if (x == endofSnakeX && y == endofSnakeY && startState.GetSnakeLength(whichSnake) >= 3)
802  {
803  startState.SetSnakeLength(whichSnake, startState.GetSnakeLength(whichSnake)-1);
804  }
805  if (GetIndex(x, y) == startState.GetSnakeHeadLoc(whichSnake) && startState.GetSnakeLength(whichSnake) >= 2)
806  {
807  startState.SetSnakeLength(whichSnake, 0);
808  startState.SetNumSnakes(startState.GetNumSnakes()-1);
809  }
810  }
811  }
812 }
813 
814 std::vector<snakeDir> LoadSnake(std:: vector<snakeDir> snakeBod, int width, int pos,std::vector<char> lvl)
815 {
816  bool snakeBuilt = false;
817  while(!snakeBuilt)
818  {
819  //build_snake:
820  if (lvl[pos+1] == 1+lvl[pos]){
821  snakeBod.push_back(kRight);
822  pos++;
823  //goto build_snake;
824  }
825  else if (lvl[pos-1] == 1+lvl[pos])
826  {
827  snakeBod.push_back(kLeft);
828  pos--;
829  //goto build_snake;
830  }
831  else if (lvl[pos+width+1] == 1+lvl[pos])
832  {
833  snakeBod.push_back(kDown);
834  pos+= width +1;
835  //goto build_snake;
836  }
837  else if (lvl[pos-width-1] == 1+lvl[pos])
838  {
839  snakeBod.push_back(kUp);
840  pos-= width +1;
841  //goto build_snake;
842  }
843  else
844  {
845  snakeBuilt = true;
846  return snakeBod;
847  }
848  }
849  assert(false);
850  return snakeBod;
851 }
852 
853 
854 
858 bool SnakeBird::Load(const char *filename)
859 {
860  BeginEditing();
861 
862  std::ifstream infile;
863  infile.open(filename);
864  if (infile.fail()) {
865  printf("File could not be opened.\n");
866  EndEditing();
867  return false;
868  }
869  if (infile.is_open()){
870  char ch, ach;
871  int x = 0;
872  int y = 0;
873  int ct = 0;
874 
875  std::string hLine;
876  getline(infile, hLine);
877 
878  int wWidth;
879  int wHeight;
880  sscanf(hLine.c_str(), "snake%dx%d", &wWidth, &wHeight);
881 
882 // std::string sHeight = hLine.substr(8,2);
883 // std::string sWidth = hLine.substr(5,2);
884 //
885 // int wWidth = std::stoi(sWidth, nullptr, 10);
886 // int wHeight = std::stoi(sHeight, nullptr,10);
887 
888  portal1Loc = portal2Loc = -1;
889  exitLoc = -1;
890  std::fill_n(&world[0], 512, kEmpty);
891  startState.Reset();
892  fruit.clear();
893  for (int x = 0; x < objects.size(); x++)
894  objects[x].clear();
895 
896  //Note that you can't call the constructor again at this point
897  //SnakeBird(wWidth, wHeight);
898  this->width = wWidth;
899  this->height = wHeight;
900  for (int x = 0; x < width; x++)
901  world[GetIndex(x, height-1)] = kSpikes;
902 
903  std::vector<char> lvlary;
904  while (infile.get(ch)) {
905 
906  lvlary.push_back(ch);
907  }
908 
909  std:: vector<snakeDir> bod;
910  bool snakeBuilt = false;
911  bool madePortal = false;
912  for(int a = 0; a <= lvlary.size(); a++)
913  {
914  if (y == height-1)
915  {
916  break;
917  }
918  int b = 0;
919  switch (lvlary[a])
920  {
921  case '.': SetGroundType(x,y,SnakeBirdWorldObject::kEmpty); x++; break;
922  case 'G': SetGroundType(x,y,SnakeBirdWorldObject::kGround); x++; break;
923  case 'S': SetGroundType(x,y,SnakeBirdWorldObject::kSpikes); x++; break;
924  case 'O':
925  if (!madePortal)
926  {
927  SetGroundType(x,y,SnakeBirdWorldObject::kPortal1);
928  madePortal = true;
929  }
930  else {
931  SetGroundType(x,y,SnakeBirdWorldObject::kPortal2);
932  }
933  x++;
934  break;
935  case 'E': SetGroundType(x,y,SnakeBirdWorldObject::kExit); x++; break;
936  case 'F': SetGroundType(x,y,SnakeBirdWorldObject::kFruit); x++; break;
937  case '1': SetGroundType(x, y, kBlock1); x++; break;
938  case '2': SetGroundType(x, y, kBlock2); x++; break;
939  case '3': SetGroundType(x, y, kBlock3); x++; break;
940  case '4': SetGroundType(x, y, kBlock4); x++; break;
941  case '\n': x = 0; y++; break;
942  case '<':
943  bod.push_back(kRight);
944  b = a;
945  b++;
946  bod = LoadSnake(bod, wWidth, b, lvlary);
947  AddSnake(x, y, bod);
948  bod.clear();
949  SetGroundType(x,y,SnakeBirdWorldObject::kEmpty);
950  x++;
951  break;
952  case '>':
953  bod.push_back(kLeft);
954  b = a;
955  b--;
956  bod = LoadSnake(bod, wWidth, b, lvlary);
957  AddSnake(x, y, bod);
958  bod.clear();
959  x++;
960  break;
961  case '^':
962  bod.push_back(kDown);
963  b = a;
964  b += wWidth+1;
965  bod = LoadSnake(bod, wWidth, b, lvlary);
966  AddSnake(x, y, bod);
967  bod.clear();
968  x++;
969  break;
970  case 'v':
971  bod.push_back(kUp);
972  b = a;
973  b -= wWidth+1;
974  bod = LoadSnake(bod, wWidth, b, lvlary);
975  AddSnake(x, y, bod);
976  bod.clear();
977  x++;
978  break;
979  default: SetGroundType(x,y,SnakeBirdWorldObject::kEmpty); x++; break;
980  }
981  }
982  infile.close();
983  EndEditing();
984  return true;
985  }
986  else {
987  std::cout << "Error loading level.";
988  EndEditing();
989  return false;
990  }
991 }
992 
996 bool SnakeBird::Save(const char *filename)
997 {
998  return false;
999 }
1000 
1001 std::string SnakeBird::Code(int v) const
1002 {
1003  //static const char* digits = "0123456789ABCDEF";
1004  static const char* digits = "abcdefghijklmnopqrstuvwxyz";
1005  std::string res;
1006  if (v >= 26*26)
1007  return "!";
1008  // res = digits[v/(26*26)];
1009  res = digits[v/26];
1010  res += digits[v%26];
1011 // printf("%d -> %s\n", v, res.c_str());
1012  return res;
1013 }
1014 
1015 int SnakeBird::DeCode(const std::string &s, size_t offset) const
1016 {
1017  int val = 0;
1018  if (s.size()-offset < codeSize)
1019  return -1;
1020  for (int x = 0; x < codeSize; x++)
1021  {
1022  switch (s[offset+x])
1023  {
1024  case 'a': val = val*26+0; break;
1025  case 'b': val = val*26+1; break;
1026  case 'c': val = val*26+2; break;
1027  case 'd': val = val*26+3; break;
1028  case 'e': val = val*26+4; break;
1029  case 'f': val = val*26+5; break;
1030  case 'g': val = val*26+6; break;
1031  case 'h': val = val*26+7; break;
1032  case 'i': val = val*26+8; break;
1033  case 'j': val = val*26+9; break;
1034  case 'k': val = val*26+10; break;
1035  case 'l': val = val*26+11; break;
1036  case 'm': val = val*26+12; break;
1037  case 'n': val = val*26+13; break;
1038  case 'o': val = val*26+14; break;
1039  case 'p': val = val*26+15; break;
1040  case 'q': val = val*26+16; break;
1041  case 'r': val = val*26+17; break;
1042  case 's': val = val*26+18; break;
1043  case 't': val = val*26+19; break;
1044  case 'u': val = val*26+20; break;
1045  case 'v': val = val*26+21; break;
1046  case 'w': val = val*26+22; break;
1047  case 'x': val = val*26+23; break;
1048  case 'y': val = val*26+24; break;
1049  case 'z': val = val*26+25; break;
1050  default: return -1; break;
1051  }
1052  }
1053 // printf("%c%c -> %d\n", s[offset], s[offset+1], val);
1054  return val;
1055 }
1056 
1060 std::string SnakeBird::EncodeLevel() const
1061 {
1062  std::string encoding;
1063  // width & height - 2 bytes each
1064  encoding += std::to_string(width);
1065  encoding += "b";
1066  encoding += std::to_string(height);
1067 
1068  // exit
1069  encoding += "E";
1070  encoding += Code(exitLoc);
1071 
1072  // ground
1073  encoding += "G";
1074  for (int i = 0; i < width*height; i++)
1075  {
1076  if (world[i] == kGround)
1077  encoding += Code(i);
1078  }
1079 
1080  // spikes
1081  encoding += "K";
1082  for (int i = 0; i < width*height; i++)
1083  {
1084  if (world[i] == kSpikes && GetY(i) != height-1)
1085  encoding += Code(i);
1086  }
1087 
1088  // fruit
1089  encoding += "F";
1090  if (fruit.size() > 0)
1091  {
1092  for (auto i : fruit)
1093  {
1094  encoding += Code(i);
1095  }
1096  }
1097 
1098  // portal
1099  if (portal1Loc != -1 && portal2Loc != -1)
1100  {
1101  encoding += "P";
1102  encoding += Code(portal1Loc);
1103  encoding += Code(portal2Loc);
1104  }
1105 
1106  // blocks
1107  for (int x = 0; x < objects.size(); x++)
1108  {
1109  if (objects[x].size() > 0)
1110  encoding += "B";
1111  for (int i = 0; i < objects[x].size(); i++)
1112  {
1113  int index = GetIndex(GetX(objects[x][i])+GetX(startState.GetObjectLocation(x)),
1114  GetY(objects[x][i])+GetY(startState.GetObjectLocation(x)));
1115  encoding += Code(index);
1116  }
1117  }
1118 
1119  // snakes
1120  for (int x = 0; x < startState.GetNumSnakes(); x++)
1121  {
1122  encoding+="S";
1123  encoding+=Code(startState.GetSnakeHeadLoc(x));
1124 
1125  for (int y = 0; y < startState.GetSnakeLength(x)-1; y++)
1126  {
1127  switch (startState.GetSnakeDir(x, y))
1128  {
1129  case kLeft: encoding+="L"; break;
1130  case kRight: encoding+="R"; break;
1131  case kDown: encoding+="D"; break;
1132  case kUp: encoding+="U"; break;
1133  }
1134  }
1135  }
1136 
1137  return encoding;
1138 }
1139 
1143 bool SnakeBird::DecodeLevel(const std::string &encoding)
1144 {
1145  portal1Loc = portal2Loc = -1;
1146  exitLoc = -1;
1147  std::fill_n(&world[0], 512, kEmpty);
1148  startState.Reset();
1149  fruit.clear();
1150  for (int x = 0; x < objects.size(); x++)
1151  objects[x].clear();
1152 
1153  BeginEditing();
1154 
1155  int cnt = sscanf(encoding.c_str(), "%db%dE", &width, &height);
1156  if (cnt != 2 || width < 5 || height < 5 || width*height > 512)
1157  { Reset(); return false; }
1158 
1159  size_t start = encoding.find_first_of("E");
1160  if (start==std::string::npos)
1161  { Reset(); printf("F1\n"); return false; }
1162  start++;
1163 
1164  exitLoc = DeCode(encoding, start);
1165  if (exitLoc == -1)
1166  {
1167  Reset();
1168  return false;
1169  }
1170  SetGroundType(exitLoc, kExit);
1171  start+=codeSize;
1172 
1173  if (start >= encoding.size() || encoding[start] != 'G')
1174  { Reset(); printf("F2\n");return false; }
1175  start++;
1176 
1177  while (start < encoding.size() && encoding[start] != 'K') // go until we see spikes
1178  {
1179  int next = DeCode(encoding, start);
1180  if (next == -1)
1181  { Reset(); printf("F3\n");return false; }
1182  start+=codeSize;
1183  SetGroundType(next, kGround);
1184  }
1185 
1186  // spikes
1187  if (start >= encoding.size() || encoding[start] != 'K')
1188  { Reset(); printf("F4\n");return false; }
1189  start++;
1190 
1191  while (start < encoding.size() && encoding[start] != 'F') // go until we see fruit
1192  {
1193  int next = DeCode(encoding, start);
1194  if (next == -1)
1195  { Reset(); printf("F5\n");return false; }
1196  start+=codeSize;
1197  SetGroundType(next, kSpikes);
1198  }
1199 
1200  if (start >= encoding.size() || encoding[start] != 'F')
1201  { Reset(); printf("F6\n");return false; }
1202  start++;
1203 
1204  while (start < encoding.size() && // might see Portal, Blocks, or Snakes next
1205  encoding[start] != 'P' && encoding[start] != 'B' && encoding[start] != 'S') // go until we see
1206  {
1207  int next = DeCode(encoding, start);
1208  if (next == -1)
1209  { Reset(); printf("F7\n");return false; }
1210  start+=codeSize;
1211  SetGroundType(next, kFruit);
1212  }
1213 
1214  // portal
1215  if (start >= encoding.size())
1216  { Reset(); printf("F8\n");return false; }
1217  if (encoding[start] == 'P' && start+1+2*codeSize <= encoding.size())
1218  {
1219  SetGroundType(DeCode(encoding, start+1), kPortal1);
1220  SetGroundType(DeCode(encoding, start+1+codeSize), kPortal2);
1221  start += 2*codeSize+1;
1222  }
1223 
1224  // blocks
1226  int whichBlock = 0;
1227  while (start < encoding.size() && encoding[start] == 'B')
1228  {
1229  start++;
1230  // Found B, read locations until we get another B or S
1231  do {
1232  int next = DeCode(encoding, start);
1233  if (next == -1)
1234  { Reset(); printf("F9\n");return false; }
1235  SetGroundType(next, blocks[whichBlock]);
1236  start+=codeSize;
1237  } while (start < encoding.size() && encoding[start] != 'B' && encoding[start] != 'S');
1238  whichBlock++;
1239  }
1240 
1241  if (encoding[start] != 'S') // snakes now
1242  { Reset(); printf("F10\n");return false; }
1243  start++;
1244 
1245  // snakes
1246  int whichSnake = 0;
1247  while (start < encoding.size())
1248  {
1249  std::vector<snakeDir> body;
1250  // Found S, read locations until we run out
1251  do {
1252  int next = DeCode(encoding, start); // head location
1253  if (next == -1)
1254  { Reset(); printf("F11\n");return false; }
1255  start += codeSize;
1256 
1257  body.clear();
1258  bool done = false;
1259  while (!done)
1260  {
1261  switch (encoding[start]) // body
1262  {
1263  case 'U': body.push_back(kUp); break;
1264  case 'D': body.push_back(kDown); break;
1265  case 'L': body.push_back(kLeft); break;
1266  case 'R': body.push_back(kRight); break;
1267  case 'S':
1268  done = true;
1269  break;
1270  }
1271  start++;
1272  if (start >= encoding.size())
1273  done = true;
1274  }
1275  if (body.size() == 0)
1276  { Reset(); printf("F12\n");return false; }
1277 
1278  startState.SetNumSnakes(whichSnake+1);
1279  startState.SetSnakeHeadLoc(whichSnake, next);
1280  startState.SetSnakeLength(whichSnake, body.size()+1);
1281  for (int x = 0; x < body.size(); x++)
1282  startState.SetSnakeDir(whichSnake, x, body[x]);
1283  whichSnake++;
1284  } while (start < encoding.size());
1285  }
1286  for (int x = 0; x < width; x++)
1287  world[GetIndex(x, height-1)] = kSpikes;
1288  EndEditing();
1289  return true;
1290 }
1291 
1292 
1296 void SnakeBird::GetSuccessors(const SnakeBirdState &nodeID, std::vector<SnakeBirdState> &neighbors) const
1297 {
1298  static std::vector<SnakeBirdAction> acts;
1299  GetActions(nodeID, acts);
1300  neighbors.clear();
1301  for (auto &a : acts)
1302  {
1303  SnakeBirdState s = nodeID;
1304  ApplyAction(s, a);
1305  bool valid = true;
1306  for (int x = s.GetNumSnakes()-1; x >= 0; x--)
1307  {
1308  if (s.GetSnakeHeadLoc(x) == kDead)
1309  {
1310  valid = false;
1311  break;
1312  }
1313  }
1314  if (valid)
1315  {
1316  // Validate that all snakes are in legal locations
1317 // for (int x = 0; x < s.GetNumSnakes(); x++)
1318 // {
1319 // if (s.GetSnakeHeadLoc(x) >= GetWidth()*GetHeight() && s.GetSnakeHeadLoc(x) < kDead)
1320 // {
1321 // for (int y = 0; y < s.GetNumSnakes(); y++)
1322 // printf("%d: %d\n", y, s.GetSnakeHeadLoc(y));
1323 // printf("Error - illegal successor\n");
1324 // s = nodeID;
1325 // ApplyAction(s, a);
1326 // }
1327 // }
1328  neighbors.push_back(s);
1329  }
1330  }
1331 }
1332 
1336 bool SnakeBird::Legal(SnakeBirdState &s, SnakeBirdAction a)
1337 {
1338  static std::vector<SnakeBirdAction> acts;
1339  GetActions(s, acts);
1340  for (auto &act : acts)
1341  if (act == a)
1342  return true;
1343  return false;
1344 }
1345 
1346 bool SnakeBird::IsOnSpikes(const SnakeBirdState &s, int snake) const
1347 {
1348  int loc = s.GetSnakeHeadLoc(snake);
1349  if (loc == kInGoal)
1350  return false;
1351  if (loc == kDead) // no legal actions for any snake if one is dead
1352  return false;
1353  if (world[loc] == kSpikes)
1354  return true;
1355  int len = s.GetSnakeBodyEnd(snake)-s.GetSnakeBodyEnd(snake-1);
1356  for (int x = 0; x < len; x++)
1357  {
1358  switch (s.GetSnakeDir(snake, x))
1359  {
1360  case kLeft: loc-=height; break;
1361  case kRight: loc+=height; break;
1362  case kUp: loc-=1; break;
1363  case kDown: loc+=1; break;
1364  }
1365  assert(loc >= 0 && loc < width*height);
1366  if (world[loc] == kSpikes)
1367  return true;
1368  }
1369  return false;
1370 }
1371 
1372 
1373 bool SnakeBird::Render(const SnakeBirdState &s) const
1374 {
1376  std::fill_n(&render[0], 512, kEmpty);
1377 
1378  // render snakes into world
1379  for (int snake = 0; snake < s.GetNumSnakes(); snake++)
1380  {
1381  int loc = s.GetSnakeHeadLoc(snake);
1382  if (loc == kInGoal)
1383  continue;
1384  if (loc == kDead) // no legal actions for any snake if one is dead
1385  return false;
1386  if (loc < 0 && loc >= width*height) // pushed offscreen
1387  return false;
1388  render[loc] = obj[snake];
1389  if (world[loc] == kSpikes)
1390  return false;
1391  int len = s.GetSnakeBodyEnd(snake)-s.GetSnakeBodyEnd(snake-1);
1392  for (int x = 0; x < len; x++)
1393  {
1394  switch (s.GetSnakeDir(snake, x))
1395  {
1396  case kLeft: loc-=height; break;
1397  case kRight: loc+=height; break;
1398  case kUp: loc-=1; break;
1399  case kDown: loc+=1; break;
1400  }
1401 // assert(loc >= 0 && loc < width*height);
1403  return false;
1404  render[loc] = obj[snake];
1405  if (world[loc] == kSpikes)
1406  return false;
1407  }
1408  }
1409  // render fruit into world
1410  for (size_t f = 0; f < fruit.size(); f++)
1411  {
1412  if (s.GetFruitPresent(f))
1413  render[fruit[f]] = kFruit;
1414  }
1415 
1416  // render objects into world
1417  for (int x = 0; x < 4; x++)
1418  {
1420  if (s.GetObjectLocation(x) == kDead)
1421  //continue; // allow pushing objects off the board
1422  return false; // disallow pushing objects off the board
1423 
1424  for (int i = 0; i < objects[x].size(); i++)
1425  {
1426  // hack for S4 - don't allow objects on the ground
1427 // if (GetY(objects[x][i])+GetY(s.GetObjectLocation(x)) >= 11)
1428 // return false;
1429 
1430  render[GetIndex(GetX(objects[x][i])+GetX(s.GetObjectLocation(x)),
1431  GetY(objects[x][i])+GetY(s.GetObjectLocation(x)))] = item[x];
1432  }
1433  }
1434  return true;
1435 }
1436 
1440 void SnakeBird::GetActions(const SnakeBirdState &s, std::vector<SnakeBirdAction> &actions) const
1441 {
1442  actions.clear();
1443  SnakeBirdAction a;
1444 
1445  bool success = Render(s);
1446  if (!success) // something illegal about state - usually has dead snakes
1447  return;
1449 
1450  for (int snake = 0; snake < s.GetNumSnakes(); snake++)
1451  {
1452  // first pass - don't allow to move back onto yourself or onto other obstacles
1453  int loc = s.GetSnakeHeadLoc(snake);
1454  if (loc == kInGoal)
1455  continue;
1456 
1457  a.bird = snake;
1458  a.pushed = 0;
1459  // kDown - can never push any object down due to gravity
1460  if (!(kGroundMask == (world[loc+1]&kGroundMask)) && // not ground or spikes
1461  GetY(loc)+1 < height && // not off screen
1462  !(kSnakeMask == (render[loc+1]&kSnakeMask)) && // not snake
1463  !(kBlockMask == (render[loc+1]&kBlockMask))) // not block
1464  {
1465  if (s.GetSnakeDir(snake, 0) != kDown) // not blocked by own snake
1466  {
1467  a.direction = kDown;
1468  actions.push_back(a);
1469  }
1470  }
1471 
1472  a.pushed = 0;
1473  // kUp
1474  if (!(kGroundMask == (world[loc-1]&kGroundMask)) && // not ground or spikes
1475  GetY(loc) > 0 && // not off screen
1476  render[loc-1] != obj[snake]) // not self
1477  {
1478  // check if snake or object which can be pushed
1479  if ((kSnakeMask == (render[loc-1]&kSnakeMask)) || (kBlockMask == (render[loc-1]&kBlockMask)))
1480  {
1481  if (CanPush(s, snake, render[loc-1], kUp, a))
1482  {
1483  a.direction = kUp;
1484  actions.push_back(a);
1485  }
1486  }
1487  else {
1488  assert(kCanEnterMask == (world[loc-1]&kCanEnterMask));
1489  a.direction = kUp;
1490  actions.push_back(a);
1491  }
1492  }
1493 
1494  a.pushed = 0;
1495  // right
1496  if (!(kGroundMask == (world[loc+height]&kGroundMask)) && // not ground or spikes
1497  GetX(loc)+1 < width && // not off screen
1498  render[loc+height] != obj[snake]) // not self
1499  {
1500  // check if snake or object which can be pushed
1501  if ((kSnakeMask == (render[loc+height]&kSnakeMask)) || (kBlockMask == (render[loc+height]&kBlockMask)))
1502  {
1503  if (CanPush(s, snake, render[loc+height], kRight, a))
1504  {
1505  a.direction = kRight;
1506  actions.push_back(a);
1507  }
1508  }
1509  else {
1510  assert(kCanEnterMask == (world[loc+height]&kCanEnterMask));
1511  a.direction = kRight;
1512  actions.push_back(a);
1513  }
1514  }
1515 
1516  a.pushed = 0;
1517  // left
1518  if (!(kGroundMask == (world[loc-height]&kGroundMask)) && // not ground or spikes
1519  GetX(loc)-1 >= 0 && // not off screen
1520  render[loc-height] != obj[snake]) // not self
1521  {
1522  // check if snake or object which can be pushed
1523  if ((kSnakeMask == (render[loc-height]&kSnakeMask)) || (kBlockMask == (render[loc-height]&kBlockMask)))
1524  {
1525  if (CanPush(s, snake, render[loc-height], kLeft, a))
1526  {
1527  a.direction = kLeft;
1528  actions.push_back(a);
1529  }
1530  }
1531  else {
1532  std::cout << +world[loc-height] << std::endl;
1533  assert(kCanEnterMask == (world[loc-height]&kCanEnterMask));
1534  a.direction = kLeft;
1535  actions.push_back(a);
1536  }
1537  }
1538  }
1539 }
1540 
1549 bool SnakeBird::CanPush(const SnakeBirdState &s, int snake, SnakeBirdWorldObject obj, snakeDir dir,
1550  SnakeBirdAction &a) const
1551 {
1552 // if (pushObject >= kMaxPushedObjects)
1553 // return false;
1554  int pushed = -1;
1555  // can't push yourself
1556  //if (pushObject+1 < kMaxPushedObjects)
1557  //a.pushed[pushObject+1] = kNothingPushed;
1558  switch (obj) {
1559  case kSnake1: if (snake == 0) return false; pushed = 0; break;
1560  case kSnake2: if (snake == 1) return false; pushed = 1; break;
1561  case kSnake3: if (snake == 2) return false; pushed = 2; break;
1562  case kSnake4: if (snake == 3) return false; pushed = 3; break;
1563  case kBlock1: pushed = 4; break;
1564  case kBlock2: pushed = 5; break;
1565  case kBlock3: pushed = 6; break;
1566  case kBlock4: pushed = 7; break;
1567  default: return false; break;
1568  }
1569  // already pushing this
1570  if ((a.pushed>>pushed)&0x1)
1571  return true;
1572 
1573  // mark object as being pushed
1574  a.pushed |= (1<<pushed);
1575 
1576  if (pushed <= 3) // snake is pushed
1577  {
1578  // Render pushed obj one step in (dir)
1579  int loc = s.GetSnakeHeadLoc(pushed);
1580  switch (dir)
1581  {
1582  case kLeft: if (loc < height) return false; loc-=height; break;
1583  case kRight: if (loc+height > width*height) return false; loc+=height; break;
1584  case kUp: if (loc == 0) return false; loc-=1; break;
1585  case kDown: if (loc+1 == width*height) return false; loc+=1; break;
1586  }
1587  // cannot push into solid object - except when going down
1588  if ((((world[loc]&kGroundMask) == kGroundMask) && dir != kDown) ||
1589  (dir == kDown && world[loc] == kGround))
1590  return false;
1591  if (render[loc]==kFruit) // cannot push into fruit
1592  return false;
1593  if (((render[loc]&kSnakeMask) == kSnakeMask) || (render[loc]&kBlockMask) == kBlockMask) // pushable object
1594  {
1595  if (!CanPush(s, snake, render[loc], dir, a))
1596  return false;
1597  }
1598 
1599  int len = s.GetSnakeBodyEnd(pushed)-s.GetSnakeBodyEnd(pushed-1);
1600  for (int x = 0; x < len; x++)
1601  {
1602  switch (s.GetSnakeDir(pushed, x))
1603  {
1604  case kLeft: if (loc < height) return false; loc-=height; break;
1605  case kRight: if (loc+height > width*height) return false; loc+=height; break;
1606  case kUp: if (loc == 0) return false; loc-=1; break;
1607  case kDown: if (loc+1 == width*height) return false; loc+=1; break;
1608  }
1609  // TODO: check if snake renders into spikes
1610  // cannot push into solid object - except when going down
1611  if ((((world[loc]&kGroundMask) == kGroundMask) && dir != kDown) ||
1612  (dir == kDown && world[loc] == kGround))
1613  return false;
1614  if (render[loc]==kFruit) // cannot push into fruit
1615  return false;
1616  if (((render[loc]&kSnakeMask) == kSnakeMask) || (render[loc]&kBlockMask) == kBlockMask) // pushable object
1617  {
1618  if (!CanPush(s, snake, render[loc], dir, a))
1619  return false;
1620  }
1621  }
1622  }
1623  else if (pushed >= 4) // object is pushed
1624  {
1625  pushed-=4;
1626  // Render pushed obj one step in (dir)
1627  int loc = s.GetObjectLocation(pushed); // s.GetSnakeHeadLoc(pushed);
1628  if (loc == kDead)
1629  return false;
1630  for (int x = 0; x < objects[pushed].size(); x++)
1631  {
1632  int pieceLoc = objects[pushed][x];
1633  pieceLoc = GetIndex(GetX(objects[pushed][x])+GetX(loc),
1634  GetY(objects[pushed][x])+GetY(loc));
1635  switch (dir)
1636  {
1637  case kLeft: if (pieceLoc < height) return false; pieceLoc-=height; break;
1638  case kRight: if (pieceLoc+height > width*height) return false; pieceLoc+=height; break;
1639  case kUp: if (pieceLoc == 0) return false; pieceLoc-=1; break;
1640  case kDown: if (pieceLoc+1 == width*height) return false; pieceLoc+=1; break;
1641  }
1642  if ((world[pieceLoc]&kGroundMask) == kGroundMask) // cannot push into solid object
1643  return false;
1644  if (render[pieceLoc]==kFruit) // cannot push into fruit
1645  return false;
1646 // if (s.GetSnakeHeadLoc(snake))
1647  if (((render[pieceLoc]&kSnakeMask) == kSnakeMask) || (render[pieceLoc]&kBlockMask) == kBlockMask) // pushable object
1648  {
1649  if (!CanPush(s, snake, render[pieceLoc], dir, a))
1650  return false;
1651  }
1652  }
1653  }
1654  // temporarily return false to not push other snakes
1655  //return ;
1656  return true;
1657 }
1658 
1659 
1660 SnakeBirdAnimation SnakeBird::DoFirstMovement(const SnakeBirdAction &a, int offset,
1661  snakeDir opposite, SnakeBirdState &s) const
1662 {
1663  bool didExit = false;
1664  if (world[s.GetSnakeHeadLoc(a.bird)+offset] == kExit && s.KFruitEaten(fruit.size()))
1665  {
1666  // move onto goal
1667  s.SetSnakeHeadLoc(a.bird, s.GetSnakeHeadLoc(a.bird)+offset);
1668  s.InsertSnakeDir(a.bird, opposite);
1669 
1670  // stop to animate exiting level]]
1671  didExit = true;
1672  // previously returned, but still need to push objects and let them fall
1673  //return;
1674  }
1675  // eating fruit (can't push and eat fruit in the same action)
1676  else if (world[s.GetSnakeHeadLoc(a.bird)+offset] == kFruit &&
1677  s.GetFruitPresent(GetFruitOffset(s.GetSnakeHeadLoc(a.bird)+offset)))
1678  {
1679  s.ToggleFruitPresent(GetFruitOffset(s.GetSnakeHeadLoc(a.bird)+offset));
1680  for (int x = a.bird; x < s.GetNumSnakes(); x++)
1681  s.SetSnakeBodyEnd(x, s.GetSnakeBodyEnd(x)+1);
1682  s.InsertSnakeHeadDir(a.bird, opposite);
1683  s.SetSnakeHeadLoc(a.bird, s.GetSnakeHeadLoc(a.bird)+offset);
1684 
1685  // If another snake is on the goal, set didEdit
1686  if (s.KFruitEaten(fruit.size()))
1687  {
1688  for (int x = 0; x < s.GetNumSnakes(); x++)
1689  {
1690  if (s.GetSnakeHeadLoc(x) == exitLoc)
1691  {
1692  didExit = true;
1693  }
1694  }
1695  }
1696  }
1697  else { // normal movement or pushing
1698  s.SetSnakeHeadLoc(a.bird, s.GetSnakeHeadLoc(a.bird)+offset);
1699  s.InsertSnakeDir(a.bird, opposite);
1700 
1701  // check for pushed snakes
1702  for (int i = 0; i < 4 && (a.pushed>>i); i++)
1703  {
1704  if ((a.pushed>>i)&0x1)
1705  {
1706  // This pushing action could push a snake offscreen, which is illegal
1707  // So, we check the boundary conditions and kill the snake if it is pushed offscreen
1708  // Snakes will later be rendered and killed if they are offscreen
1709  if (s.GetSnakeHeadLoc(i)+offset < 0 || s.GetSnakeHeadLoc(i)+offset >= GetWidth()*GetHeight())
1710  s.SetSnakeHeadLoc(i, kDead);
1711  else if (abs(GetX(s.GetSnakeHeadLoc(i))-GetX(s.GetSnakeHeadLoc(i)+offset)) > 1)
1712  s.SetSnakeHeadLoc(i, kDead);
1713  else if (abs(GetY(s.GetSnakeHeadLoc(i))-GetY(s.GetSnakeHeadLoc(i)+offset)) > 1)
1714  s.SetSnakeHeadLoc(i, kDead);
1715  else
1716  s.SetSnakeHeadLoc(i, s.GetSnakeHeadLoc(i)+offset);
1717 
1718  if (world[s.GetSnakeHeadLoc(i)] == kExit && s.KFruitEaten(fruit.size()))
1719  {
1720  didExit = true;
1721  //s.SetSnakeHeadLoc(i, kInGoal);
1722  }
1723  }
1724  }
1725  // check for pushed objects
1726  for (int i = 4; i < 8 && (a.pushed>>i); i++)
1727  {
1728  if ((a.pushed>>i)&0x1)
1729  {
1730  if (s.GetObjectLocation(i-4) == kDead)
1731  continue;
1732  int newloc = s.GetObjectLocation(i-4)+offset;
1733  s.SetObjectLocation(i-4, newloc);
1734  }
1735  }
1736  }
1737  if (didExit)
1738  return kWentInGoal;
1739  else
1740  return kInitialTeleport;
1741 
1742 }
1743 
1744 SnakeBirdAnimation SnakeBird::DoFall(SnakeBirdAction &a, SnakeBirdState &s) const
1745 {
1747  uint8_t falling = 0;
1748  SnakeBirdAction move;
1749  // Find which objects can fall
1750  for (int i = 0; i < 8; i++)
1751  {
1752  move.pushed = 0;
1753  // snake isn't in the world
1754  if ((i < 4 && i >= s.GetNumSnakes()) || s.GetSnakeHeadLoc(i) == kInGoal)
1755  continue;
1756  // block isn't in the world (fell out or not there to begin with)
1757  if (i >= 4 && (objects[i-4].size() == 0 || s.GetObjectLocation(i-4) == kDead))
1758  continue;
1759 
1760  if (CanPush(s, -1, objs[i], kDown, move))
1761  {
1762  falling |= move.pushed;
1763  }
1764  }
1765  // If nothing can fall, we are done
1766  if (falling == 0)
1767  return kDoneAnimation;
1768 
1769  // Actually move the objects down
1770  bool hitExit = false;
1771  for (int i = 0; i < 8; i++)
1772  {
1773  if (i < 4 && ((falling>>i)&0x1))
1774  {
1775  s.SetSnakeHeadLoc(i, s.GetSnakeHeadLoc(i)+1);
1776  if (world[s.GetSnakeHeadLoc(i)] == kExit && s.KFruitEaten(fruit.size()))
1777  {
1778  hitExit = true;
1779 // s.SetSnakeHeadLoc(i, kInGoal);
1780  }
1781  }
1782  if (i >= 4 && ((falling>>i)&0x1))
1783  s.SetObjectLocation(i-4, s.GetObjectLocation(i-4)+1);
1784  }
1785 
1786  a.pushed = falling;
1787 
1788  // check if an object hit the bottom of the world - if so it is dead
1789  for (size_t i = 0; i < objects.size(); i++)
1790  {
1791  int objLoc = s.GetObjectLocation(i);
1792  if (objLoc == kDead)
1793  continue;
1794  int objectY = GetY(objLoc);
1795  for (size_t j = 0; j < objects[i].size(); j++)
1796  {
1797  if (objectY+GetY(objects[i][j]) >= height-2)
1798  {
1799  s.SetObjectLocation(i, kDead);
1800  break;
1801  }
1802  }
1803  }
1804 
1805  if (hitExit)
1806  return kFellInGoal;
1807 
1808  return kTeleport;
1809 }
1810 
1814 bool SnakeBird::ApplyPartialAction(SnakeBirdState &s, SnakeBirdAction act, SnakeBirdAnimationStep &step) const
1815 {
1816 // ApplyAction(s, act);
1817 // return true;
1818  if (step.anim == kPauseWhenDead)
1819  return true;
1820 
1821 
1822  if (step.anim == kNeedsInitialization)
1823  {
1824  step.anim = kMovement;
1825  step.a = act;
1826  }
1827  int offset;
1828  snakeDir opposite;
1829  snakeDir lastAction = (snakeDir)act.direction;
1830  switch (step.a.direction)
1831  {
1832  case kLeft: offset = -height; opposite = kRight; break;
1833  case kRight: offset = +height; opposite = kLeft; break;
1834  case kUp: offset = -1; opposite = kDown; break;
1835  case kDown: offset = 1; opposite = kUp; break;
1836  default: printf("Warning: Applied illegal action\n"); return true;
1837  }
1838 
1839  if (step.anim == kMovement)
1840  {
1841  // exited level
1842  step.anim = DoFirstMovement(step.a, offset, opposite, s);
1843  step.animationDuration = 0.1;
1844  return false;
1845  }
1846 
1847  if (step.anim == kWentInGoal)
1848  {
1849  for (int i = 0; i < 4; i++)
1850  {
1851  if (world[s.GetSnakeHeadLoc(i)] == kExit && s.KFruitEaten(fruit.size()))
1852  {
1853  s.SetSnakeHeadLoc(i, kInGoal);
1854  step.animationDuration = s.GetSnakeLength(i)*0.1;
1855  if (step.animationDuration < 0)
1856  step.animationDuration = 0.01;
1857  break;
1858  }
1859  }
1860  step.anim = kInitialTeleport;
1861  return false;
1862  }
1863 
1864 
1865  // Small hack to make the following loop more generic:
1866  // Add current bird to list of pushed birds
1867  // This will cause us to check it for portals
1868  if (step.anim == kInitialTeleport)
1869  {
1870  step.a.pushed |= (1<<step.a.bird);
1871  }
1872  else {
1873  lastAction = kDown;
1874  opposite = kUp;
1875  }
1876 
1877  // Now for gravity and portals.
1878  // 1. Check if any portals are activated.
1879  // 2. Then try all moved objects to see if they can be pushed down.
1880  // 2a. Merge the pushable objects into a single bit vector
1881  // 3. If no objects moved, stop; else repeat to 1.
1882  bool success = Render(s);
1883  if (!success) // someone died as a result of the action
1884  {
1885  step.animationDuration = 0.5; // then undo automatically
1886  step.anim = kPauseWhenDead;
1887  return false;
1888  }
1889 
1890  if (step.anim == kTeleport || step.anim == kInitialTeleport)
1891  {
1892  auto didTeleport = HandleTeleports(s, step.a, lastAction, opposite, step);
1893  if (didTeleport == kTeleportSuccess)
1894  {
1895  step.anim = kFall;
1896  step.animationDuration = 0.1;
1897  step.teleportCount++;
1898  return false;
1899  }
1900  else if (didTeleport == kTeleportToExit)
1901  {
1902  step.anim = kFellInGoal;
1903  step.animationDuration = 0.1;
1904  return false;
1905  }
1906  step.anim = kFall;
1907  }
1908 
1909  if (step.teleportCount > 10)
1910  {
1911  for (int x = 0; x < s.GetNumSnakes(); x++)
1912  s.SetSnakeHeadLoc(x, kDead);
1913  step.animationDuration = 0.5; // then undo automatically
1914  step.anim = kPauseWhenDead;
1915  return false;
1916  }
1917 
1918  if (step.anim == kFall)
1919  {
1920  step.anim = DoFall(step.a, s);
1921  if (step.anim == kDoneAnimation)
1922  return true;
1923  step.animationDuration = 0.05; // then undo automatically
1924  return false;
1925  }
1926 
1927  if (step.anim == kFellInGoal)
1928  {
1929  for (int i = 0; i < 4; i++)
1930  {
1931  if (world[s.GetSnakeHeadLoc(i)] == kExit && s.KFruitEaten(fruit.size()))
1932  {
1933  s.SetSnakeHeadLoc(i, kInGoal);
1934  step.animationDuration = s.GetSnakeLength(i)*0.1;
1935  if (step.animationDuration < 0)
1936  step.animationDuration = 0.01;
1937  break;
1938  }
1939  }
1940  step.anim = kFall;
1941  }
1942 
1943  return false;
1944 }
1945 
1949 //SnakeBirdAction GetAction(const SnakeBirdState &s1, const SnakeBirdState &s2) const;
1950 void SnakeBird::ApplyAction(SnakeBirdState &s, SnakeBirdAction a) const
1951 {
1953  step.anim = kMovement;
1954  int offset;
1955  snakeDir opposite;
1956  snakeDir lastAction = (snakeDir)a.direction;
1957  switch (a.direction)
1958  {
1959  case kLeft: offset = -height; opposite = kRight; break;
1960  case kRight: offset = +height; opposite = kLeft; break;
1961  case kUp: offset = -1; opposite = kDown; break;
1962  case kDown: offset = 1; opposite = kUp; break;
1963  default: printf("Warning: Applied illegal action\n"); return;
1964  }
1965 
1966  if (step.anim == kMovement)
1967  {
1968  // exited level
1969  step.anim = DoFirstMovement(a, offset, opposite, s);
1970  }
1971 
1972  if (step.anim == kWentInGoal)
1973  {
1974  for (int i = 0; i < 4; i++)
1975  {
1976  if (world[s.GetSnakeHeadLoc(i)] == kExit && s.KFruitEaten(fruit.size()))
1977  {
1978  s.SetSnakeHeadLoc(i, kInGoal);
1979  }
1980  }
1981  step.anim = kInitialTeleport;
1982  }
1983 
1984  // Small hack to make the following loop more generic:
1985  // Add current bird to list of pushed birds
1986  // This will cause us to check it for portals
1987  a.pushed |= (1<<a.bird);
1988 
1989  // Now for gravity and portals.
1990  // 1. Check if any portals are activated.
1991  // 2. Then try all moved objects to see if they can be pushed down.
1992  // 2a. Merge the pushable objects into a single bit vector
1993  // 3. If no objects moved, stop; else repeat to 1.
1994  while (true) {
1995  bool success = Render(s);
1996  if (!success) // someone died as a result of the action
1997  return;
1998 
1999  if (step.anim == kTeleport)
2000  {
2001  auto didTeleport = HandleTeleports(s, a, kDown, kUp, step);
2002  if (didTeleport == kTeleportSuccess)
2003  {
2004  step.teleportCount++;
2005  step.anim = kFall;
2006  }
2007  else if (didTeleport == kTeleportToExit)
2008  {
2009  step.anim = kFellInGoal;
2010  }
2011  else {
2012  step.anim = kFall;
2013  }
2014  }
2015  else if (step.anim == kInitialTeleport) {
2016  auto didTeleport = HandleTeleports(s, a, lastAction, opposite, step);
2017  if (didTeleport == kTeleportSuccess)
2018  {
2019  step.teleportCount++;
2020  step.anim = kFall;
2021  }
2022  else if (didTeleport == kTeleportToExit)
2023  {
2024  step.anim = kFellInGoal;
2025  }
2026  else {
2027  step.anim = kFall;
2028  }
2029  }
2030 
2031  if (step.teleportCount > 10)
2032  {
2033  for (int x = 0; x < s.GetNumSnakes(); x++)
2034  s.SetSnakeHeadLoc(x, kDead);
2035  return;
2036  }
2037 
2038  if (step.anim == kFall)
2039  {
2040  step.anim = DoFall(a, s);
2041  if (step.anim == kDoneAnimation)
2042  return;
2043  }
2044 
2045  if (step.anim == kFellInGoal)
2046  {
2047  // check if we died at the same time
2048  bool success = Render(s);
2049  if (!success)
2050  return;
2051 
2052  for (int i = 0; i < 4; i++)
2053  {
2054  if (world[s.GetSnakeHeadLoc(i)] == kExit && s.KFruitEaten(fruit.size()))
2055  {
2056  s.SetSnakeHeadLoc(i, kInGoal);
2057  }
2058  }
2059  step.anim = kFall;
2060  }
2061 
2062  // step.anim = kTeleport;
2063  }
2064 }
2065 
2066 TeleportResult SnakeBird::HandleTeleports(SnakeBirdState &s, SnakeBirdAction &a,
2067  snakeDir lastAction, snakeDir opposite, SnakeBirdAnimationStep step) const
2068 {
2070  bool didTeleport = false;
2071  bool didExit = false;
2072  // Check for snakes which are now on portals
2073  // *and* didn't have an adjacent segment on a portal in the last time step
2074  for (int i = 0; i < 4 && (a.pushed>>i); i++)
2075  {
2076  int snake = i;
2077  if ((a.pushed>>i)&0x1) // ignore if it wasn't moved
2078  {
2079  int newloc = s.GetSnakeHeadLoc(snake);
2080  int piece = newloc;
2081  for (int x = 0; x < s.GetSnakeLength(snake); x++)
2082  {
2083  //int lastPiece = piece;
2084  // in the first round of gravity/portals
2085  // only check the head of the bird that was moved
2086  if (i == a.bird && step.anim == kInitialTeleport && x > 0)
2087  {
2088  break;
2089  }
2090  if (x > 0)
2091  {
2092  switch (s.GetSnakeDir(snake, x-1))
2093  {
2094  case kLeft: piece-=height; break;
2095  case kRight: piece+=height; break;
2096  case kUp: piece-=1; break;
2097  case kDown: piece+=1; break;
2098  }
2099  }
2100  int p1, p2 = -1;
2101  if (piece == portal1Loc)
2102  {
2103  p1 = portal1Loc;
2104  p2 = portal2Loc;
2105  }
2106  else if (piece == portal2Loc)
2107  {
2108  p1 = portal2Loc;
2109  p2 = portal1Loc;
2110  }
2111 
2112  // Did the snake just move to adjacent locations?
2113  if (p2 != -1)
2114  {
2115  // We are looking at the snake location after it was moved.
2116  // Need to check in forward and backwards directions
2117  if (x > 0 && s.GetSnakeDir(snake, x-1) == opposite)
2118  { p2 = -1; }
2119  else if (x < s.GetSnakeLength(snake)-1 && s.GetSnakeDir(snake, x) == lastAction)
2120  { p2 = -1; }
2121  }
2122 
2123  // We have a portal to teleport to
2124  if (p2 != -1)
2125  {
2126  int xChange = -GetX(p1)+GetX(p2);
2127  int yChange = -GetY(p1)+GetY(p2);
2128 
2129  // Validate if the object can be placed in the new location
2130  bool valid = true;
2131  int newPiece = GetIndex(GetX(newloc)+xChange, GetY(newloc)+yChange);
2132  //for (int v = 0; v < objects[snake].size(); v++)
2133  for (int v = 0; v < s.GetSnakeLength(snake); v++)
2134  {
2135  if (v > 0)
2136  {
2137  switch (s.GetSnakeDir(snake, v-1))
2138  {
2139  case kLeft: newPiece-=height; break;
2140  case kRight: newPiece+=height; break;
2141  case kUp: newPiece-=1; break;
2142  case kDown: newPiece+=1; break;
2143  }
2144  }
2145 
2146  if (GetX(newPiece) < 0 ||
2147  GetX(newPiece) >= width ||
2148  GetY(newPiece) < 0 ||
2149  GetY(newPiece) >= height)
2150  {
2151  valid = false;
2152  break;
2153  }
2154 
2155  if (((render[newPiece]&kSnakeMask) == kSnakeMask &&
2156  render[newPiece] != objs[snake]) || // hit another snake
2157  ((world[newPiece]&kGroundMask) == kGroundMask) || // hit ground
2158  ((render[newPiece]&kBlockMask) == kBlockMask) || // hit piece
2159  (render[newPiece] == kFruit)) // hit fruit
2160  {
2161  valid = false;
2162  break;
2163  }
2164  }
2165  if (valid)
2166  {
2167  int offsetFromPortalX = GetX(newloc)+xChange;
2168  int offsetFromPortalY = GetY(newloc)+yChange;
2169  s.SetSnakeHeadLoc(i, GetIndex(offsetFromPortalX, offsetFromPortalY));
2170  if (s.GetSnakeHeadLoc(i) == exitLoc && s.KFruitEaten(fruit.size()))
2171  didExit = true;
2172  didTeleport = true;
2173  bool success = Render(s);
2174  assert(success == true);
2175 // if (!success) // someone died as a result of the action
2176 // return;
2177  }
2178  }
2179  }
2180  }
2181  }
2182 
2183  // Check for objects which are now on portals
2184  // and didn't have an adjacent segment on a portal in the last step
2185  // If an object teleports we have to re-render
2186  // Note - only one object can teleport, so don't check if a snake teleported
2187  for (int i = 4; i < 8 && (a.pushed>>i) && !didTeleport; i++)
2188  {
2189  int object = i-4;
2190  if ((a.pushed>>i)&0x1) // ignore if it wasn't moved
2191  {
2192  int newloc = s.GetObjectLocation(object);
2193  if (newloc == kDead)
2194  continue;
2195  for (size_t x = 0; x < objects[object].size(); x++)
2196  {
2197  int piece = GetIndex(GetX(objects[object][x])+GetX(newloc),
2198  GetY(objects[object][x])+GetY(newloc));
2199  int p1=-1, p2 = -1;
2200  if (piece == portal1Loc)
2201  {
2202  p1 = portal1Loc;
2203  p2 = portal2Loc;
2204  }
2205  else if (piece == portal2Loc)
2206  {
2207  p1 = portal2Loc;
2208  p2 = portal1Loc;
2209  }
2210  // Check if our last action suggests that the object was in the portal previously
2211  if (p2 != -1)
2212  {
2213  switch (lastAction)
2214  {
2215  case kLeft:
2216  if (p1-height >= 0 && render[p1-height] == objs[i]) // can't teleport
2217  p2 = -1;
2218  break;
2219  case kRight:
2220  if (p1+height < width*height && render[p1+height] == objs[i]) // can't teleport
2221  p2 = -1;
2222  break;
2223  case kUp:
2224  if (p1-1 >= 0 && render[p1-1] == objs[i]) // can't teleport
2225  p2 = -1;
2226  break;
2227  case kDown:
2228  if (p1+1 < width*height && render[p1+1] == objs[i]) // can't teleport
2229  p2 = -1;
2230  break;
2231  }
2232  }
2233 
2234  if (p2 != -1)
2235  {
2236  // TODO: Check if it can be placed in new loc relative to self
2237  // TODO: Check if it is offscreen
2238  int xChange = -GetX(p1)+GetX(p2);
2239  int yChange = -GetY(p1)+GetY(p2);
2240 
2241  // Validate if the object can be placed in the new location
2242  bool valid = true;
2243  for (size_t v = 0; v < objects[object].size(); v++)
2244  {
2245  if (GetX(newloc)+GetX(objects[object][v])+xChange < 0 ||
2246  GetX(newloc)+GetX(objects[object][v])+xChange >= width ||
2247  GetY(newloc)+GetY(objects[object][v])+yChange < 0 ||
2248  GetY(newloc)+GetY(objects[object][v])+yChange >= height)
2249  {
2250  valid = false;
2251  break;
2252  }
2253  int newPiece = GetIndex(GetX(newloc)+GetX(objects[object][v])+xChange,
2254  GetY(newloc)+GetY(objects[object][v])+yChange);
2255  if (((render[newPiece]&kSnakeMask) == kSnakeMask) || // hit snake
2256  ((world[newPiece]&kGroundMask) == kGroundMask) || // hit ground
2257  ((render[newPiece]&kBlockMask) == kBlockMask && render[newPiece] != objs[i])) // hit another piece
2258  {
2259  valid = false;
2260  break;
2261  }
2262  }
2263  if (valid)
2264  {
2265  int offsetFromPortalX = GetX(newloc)+xChange;
2266  int offsetFromPortalY = GetY(newloc)+yChange;
2267  s.SetObjectLocation(object, GetIndex(offsetFromPortalX, offsetFromPortalY));
2268  didTeleport = true;
2269 
2270  bool success = Render(s);
2271  assert(success == true);
2272 // if (!success) // someone died as a result of the action
2273 // return;
2274  }
2275  }
2276  }
2277  }
2278  }
2279  if (didExit && didTeleport)
2280  return kTeleportToExit;
2281  if (didTeleport)
2282  return kTeleportSuccess;
2283  return kNoTeleport;
2284 // return didTeleport;
2285 }
2286 
2287 void SnakeBird::SetGroundType(int index, SnakeBirdWorldObject o)
2288 {
2289  SetGroundType(GetX(index), GetY(index), o);
2290 }
2291 
2295 void SnakeBird::RemoveBlock(int x, int y)
2296 {
2297  for (int which = 0; which < 4; which++)
2298  {
2299  if (objects[which].size() <= 0) // no other objects
2300  continue;
2301 
2302  int xOffset = 0, yOffset = 0;
2303  xOffset = GetX(startState.GetObjectLocation(which));
2304  yOffset = GetY(startState.GetObjectLocation(which));
2305 
2306  for (size_t i = 0; i < objects[which].size(); i++)
2307  {
2308  if (GetX(objects[which][i])+xOffset == x &&
2309  GetY(objects[which][i])+yOffset == y) // remove
2310  {
2311  objects[which][i] = objects[which].back();
2312  objects[which].pop_back();
2313  break;
2314  }
2315  }
2316  // quit loop if we removed last object
2317  if (objects[which].size() <= 0)
2318  {
2319  continue;
2320  }
2321 
2322  int newX = width, newY=height;
2323 
2324  // Get new base location (minx/y)
2325  for (size_t i = 0; i < objects[which].size(); i++)
2326  {
2327  newX = std::min(newX, GetX(objects[which][i])+xOffset);
2328  newY = std::min(newY, GetY(objects[which][i])+yOffset);
2329  }
2330  startState.SetObjectLocation(which, GetIndex(newX, newY));
2331  for (size_t i = 0; i < objects[which].size(); i++)
2332  {
2333  // reset locations based on new base location
2334  objects[which][i] = GetIndex(GetX(objects[which][i])+xOffset - newX,
2335  GetY(objects[which][i])+yOffset - newY);
2336  }
2337  }
2338 }
2339 
2343 void SnakeBird::SetGroundType(int x, int y, SnakeBirdWorldObject o)
2344 {
2345  if (!editing)
2346  {
2347  printf("!!WARNING: Cannot change map without enabling editing!!\n");
2348  return;
2349  }
2350  // TODO: check duplicates and removals for fruit and blocks
2351  if (y == height-1)
2352  {
2353  printf("Error - cannot add terrain at bottom of map\n");
2354  return;
2355  }
2356 
2357  if ((o&kBlockMask) == kBlockMask) // are you adding a moveable block?
2358  {
2359  int which = -1;
2360  switch (o)
2361  {
2362  case kBlock1:
2363  which = 0;
2364  break;
2365  case kBlock2:
2366  which = 1;
2367  break;
2368  case kBlock3:
2369  which = 2;
2370  break;
2371  case kBlock4:
2372  which = 3;
2373  break;
2374  default:
2375  assert(!"Can't get here - object is a block");
2376  break;
2377  }
2378  assert(which != -1);
2379  // objects are stored as offsets from 0.
2380  // The offset in the original world is in startState
2381  int newX = x, newY=y;
2382  int xOffset = 0, yOffset = 0;
2383  if (objects[which].size() > 0) // other objects - get their offset base
2384  {
2385  xOffset = GetX(startState.GetObjectLocation(which));
2386  yOffset = GetY(startState.GetObjectLocation(which));
2387  }
2388 
2389  // Get new base location (minx/y)
2390  for (int i = 0; i < objects[which].size(); i++)
2391  {
2392  newX = std::min(newX, GetX(objects[which][i])+xOffset);
2393  newY = std::min(newY, GetY(objects[which][i])+yOffset);
2394  }
2395  startState.SetObjectLocation(which, GetIndex(newX, newY));
2396  for (int i = 0; i < objects[which].size(); i++)
2397  {
2398  // reset locations based on new base location
2399  objects[which][i] = GetIndex(GetX(objects[which][i])+xOffset - newX,
2400  GetY(objects[which][i])+yOffset - newY);
2401  }
2402  // add new piece of new object
2403  objects[which].push_back(GetIndex(x-newX, y-newY));
2404 
2405  return;
2406  }
2407 
2408  if (o == kPortal)
2409  {
2410  if ((portal1Loc == -1) || (portal1Loc == GetIndex(x, y)))
2411  {
2412  o = kPortal1;
2413  }
2414  else if ((portal2Loc == -1) || (portal2Loc == GetIndex(x, y)))
2415  {
2416  o = kPortal2;
2417  }
2418  else {
2419  printf("error cannot determine portal type");
2420  return;
2421  }
2422  }
2423  auto oldTerrainType = world[GetIndex(x, y)];
2424  world[GetIndex(x, y)] = o;
2425  // TODO: IF old ground was fruit, find it in the fruit array and remove it by
2426  // taking the last item out of the array and putting it in place of the one removed,
2427  // and then shrinking the size by one. -> resize(size()-1)
2428 
2429  // TODO: If old type was the exit, reset the exit location to -1.
2430 
2431  if (oldTerrainType == kFruit)
2432  {
2433  std::vector<int>::iterator tor = std::find(fruit.begin(), fruit.end(), GetIndex(x, y));
2434  if (tor != fruit.end())
2435  {
2436  fruit.erase(tor);
2437  }
2438  }
2439  if (o == kFruit)
2440  fruit.push_back(GetIndex(x, y));
2441  if (oldTerrainType == kExit)
2442  exitLoc = -1;
2443  if (o == kExit)
2444  {
2445  if (exitLoc != -1 && exitLoc != GetIndex(x, y))
2446  world[exitLoc] = kEmpty;
2447  exitLoc = GetIndex(x, y);
2448  }
2449  if (o == kPortal1)
2450  {
2451  if (portal1Loc != -1 && portal1Loc != GetIndex(x, y))
2452  world[portal1Loc] = kEmpty;
2453  portal1Loc = GetIndex(x, y);
2454  }
2455  if (o == kPortal2)
2456  {
2457  if (portal2Loc != -1 && portal2Loc != GetIndex(x, y))
2458  world[portal2Loc] = kEmpty;
2459  portal2Loc = GetIndex(x, y);
2460  }
2461  if (oldTerrainType == kPortal1 && portal1Loc == GetIndex(x, y))
2462  {
2463  world[portal1Loc] = kEmpty;
2464  portal1Loc = -1;
2465  }
2466  if (oldTerrainType == kPortal2 && portal2Loc == GetIndex(x, y))
2467  {
2468  world[portal2Loc] = kEmpty;
2469  portal2Loc = -1;
2470  }
2471 }
2472 
2473 int SnakeBird::GetNumPortals()
2474 {
2475  int cnt = 0;
2476  if (portal1Loc != -1)
2477  cnt++;
2478  if (portal2Loc != -1)
2479  cnt++;
2480  return cnt;
2481 // if (portal1Loc == -1 || portal2Loc == -1)
2482 // return true;
2483 // else
2484 // return false;
2485 }
2486 
2487 int SnakeBird::GetFruitOffset(int index) const
2488 {
2489  for (int x = 0; x < fruit.size(); x++)
2490  if (fruit[x] == index)
2491  return x;
2492  assert(false);
2493  return -1;
2494 }
2495 
2496 
2500 SnakeBirdWorldObject SnakeBird::GetGroundType(int x, int y) const
2501 {
2502  return world[GetIndex(x, y)];
2503 }
2504 
2508 SnakeBirdWorldObject SnakeBird::GetRenderedGroundType(const SnakeBirdState &s, int x, int y)
2509 {
2510  if (GetGroundType(x, y) != kEmpty)// || GetGroundType(x, y) == kPortal1 || GetGroundType(x, y) == kPortal2)
2511  return GetGroundType(x, y);
2512  bool success = Render(s);
2513  if (!success)
2514  return kExit;
2515  // assert(success);
2516  return render[GetIndex(x, y)];
2517 }
2518 
2519 
2520 int SnakeBird::GetIndex(int x, int y) const
2521 {
2522  return x*height+y;
2523 }
2524 
2525 int SnakeBird::GetX(int index) const
2526 {
2527  return index/height;
2528 }
2529 
2530 int SnakeBird::GetY(int index) const
2531 {
2532  return index%height;
2533 }
2534 
2535 int SnakeBird::Distance(int index1, int index2)
2536 {
2537  int x1 = GetX(index1);
2538  int y1 = GetY(index1);
2539  int x2 = GetX(index2);
2540  int y2 = GetY(index2);
2541  return abs(x1-x2)+abs(y1-y2);
2542 }
2543 
2544 void SnakeBird::Draw(Graphics::Display &display, int x, int y, float width) const
2545 {
2546  Graphics::point p = GetCenter(x, y);
2547  display.FrameRect({p, GetRadius()}, GetColor(), GetRadius()*0.3*width);
2548 }
2549 
2550 void SnakeBird::Draw(Graphics::Display &display) const
2551 {
2552  display.FillRect({-1.1, -1.1, 1.1, 1.1}, rgbColor::mix(Colors::cyan, Colors::lightblue, 0.5));
2553  Graphics::point tmp = GetCenter(width, height);
2554  display.FillRect({-1.1, tmp.y-GetRadius(), 1.1, 1.1}, Colors::darkblue+Colors::darkred);
2555  for (int x = 0; x < width*height; x++)
2556  {
2557  Graphics::point p = GetCenter(GetX(x), GetY(x));
2558  float radius = GetRadius()*0.95;
2559  switch (world[x])
2560  {
2561  case kEmpty:
2562  break;//display.FillSquare(p, GetRadius(), Colors::lightblue); break;
2563  case kGround:
2564  switch ((GetX(x)*13*+11*GetY(x))%6)
2565  {
2566  case 0: display.FillSquare(p, GetRadius(), Colors::brown); break;
2567  case 1: display.FillSquare(p, GetRadius(), Colors::brown); break;
2568  case 2: display.FillSquare(p, GetRadius(), Colors::brown+Colors::gray); break;
2569  case 3: display.FillSquare(p, GetRadius(), Colors::brown+Colors::gray); break;
2570  case 4: display.FillSquare(p, GetRadius(), Colors::brown*0.8+Colors::darkgreen); break;
2571  case 5: display.FillSquare(p, GetRadius(), Colors::brown*0.8+Colors::darkgreen); break;
2572  }
2573  break;
2574  case kSpikes:
2575  if (GetY(x) != height-1)
2576  {
2577 // radius = GetRadius();
2578 // display.FillNGon(p, radius, 3, 0, Colors::darkgray);
2579 // display.FillNGon(p, radius, 3, 60, Colors::gray*0.75f);
2580  display.FillSquare({p.x, p.y+radius/2.0f}, radius/2.0f, Colors::darkgray);
2581  display.FillTriangle({p.x-radius/2.0f, p.y}, {p.x-radius/4.0f, p.y-radius/2}, {p.x, p.y}, Colors::darkred);
2582  display.FillTriangle({p.x+radius/2.0f, p.y}, {p.x+radius/4.0f, p.y-radius/2}, {p.x, p.y}, Colors::darkred);
2583 
2584  display.FillTriangle({p.x-radius/2.0f, p.y}, {p.x-radius, p.y+radius/4}, {p.x-radius/2.0f, p.y+radius/2}, Colors::darkred);
2585  display.FillTriangle({p.x-radius/2.0f, p.y+radius/2}, {p.x-radius, p.y+3*radius/4}, {p.x-radius/2.0f, p.y+radius}, Colors::darkred);
2586 
2587  display.FillTriangle({p.x+radius/2.0f, p.y}, {p.x+radius, p.y+radius/4}, {p.x+radius/2.0f, p.y+radius/2}, Colors::darkred);
2588  display.FillTriangle({p.x+radius/2.0f, p.y+radius/2}, {p.x+radius, p.y+3*radius/4}, {p.x+radius/2.0f, p.y+radius}, Colors::darkred);
2589  }
2590  break;
2591  default: break;
2592  }
2593  }
2594 }
2595 
2596 void SnakeBird::DrawObjects(Graphics::Display &display, double time) const
2597 {
2598  for (int x = 0; x < width*height; x++)
2599  {
2600  Graphics::point p = GetCenter(GetX(x), GetY(x));
2601  double radius = GetRadius()*0.95;
2602  switch (world[x])
2603  {
2604  case kEmpty:
2605  break;//display.FillSquare(p, GetRadius(), Colors::lightblue); break;
2606  case kGround:
2607  display.FillSquare(p, GetRadius(), Colors::brown); break;
2608  break;
2609  case kSpikes:
2610  if (GetY(x) != height-1)
2611  {
2612  display.FillNGon(p, radius, 3, 0, Colors::darkgray);
2613  display.FillNGon(p, radius, 3, 60, Colors::gray*0.75f);
2614  }
2615  break;
2616  case kPortal1:
2617  case kPortal2:
2618  {
2619  double radius = GetRadius()*0.95;
2620  Graphics::point p = GetCenter(GetX(x), GetY(x));
2621  double offset1 = (sin(time*1.0)); // -1...+1
2622  double offset2 = (sin(time*1.5)); // -1...+1
2623  double offset3 = (sin(time*2.0)); // -1...+1
2624  double offset4 = (sin(time*2.5)); // -1...+1
2625  display.FillCircle(p, radius+offset1*radius*0.25, Colors::red);
2626  display.FillCircle(p, radius*0.75+offset2*radius*0.2, Colors::blue);
2627  display.FillCircle(p, radius*0.5+offset3*radius*0.15, Colors::green);
2628  display.FillCircle(p, radius*0.25+offset4*radius*0.1, Colors::purple);
2629  }
2630  break;
2631  case kFruit:
2632  {
2633  const float rows = 5;
2634  const float counts[] = {3, 4, 3, 2, 1};
2635  Graphics::point p = GetCenter(GetX(x), GetY(x));
2636  float grapeRadius = GetRadius()*0.15f;
2637  for (int t = 0; t < rows; t++)
2638  {
2639  for (int v = 0; v < counts[t]; v++)
2640  {
2641  Graphics::point tmp = p;
2642  tmp.y -= rows*grapeRadius;
2643  tmp.y += 2*t*grapeRadius;
2644  tmp.x = tmp.x-grapeRadius*(counts[t]-1)+v*grapeRadius*2;
2645  tmp.x += sin(time*1.1+(t+v)*0.1)*GetRadius()*0.1;
2646  tmp.y += cos(time*0.95+(t+v)*0.1)*GetRadius()*0.1;
2647  display.FillCircle(tmp, grapeRadius*1.1, Colors::purple*sin(time*1.95+v+t));
2648  }
2649  }
2650  }
2651  break;
2652  case kExit:
2653  {
2654  Graphics::point p = GetCenter(GetX(exitLoc), GetY(exitLoc));
2655  double radius = GetRadius()*0.95;
2656  double localTime = time;
2657  localTime /= 4.0;
2658  double offset1 = (sin(localTime*1.0))*180; // -1...+1
2659  double offset2 = (sin(localTime*1.5))*180; // -1...+1
2660  double offset3 = (sin(localTime*2.0))*180; // -1...+1
2661  display.FillNGon(p, radius, 5, 0+offset1, Colors::yellow);
2662  display.FillNGon(p, radius*0.66, 5, 36+offset2, Colors::orange);
2663  display.FillNGon(p, radius*0.25, 5, 54+offset3, Colors::red);
2664  break;
2665  }
2666  default: break;
2667  }
2668  }
2669 }
2670 
2671 void SnakeBird::DrawObject(Graphics::Display &display, int x, int y, SnakeBirdWorldObject o, double time) const
2672 //TODO: add ksnake1 - call the draw command for drawing the snakes - search for ksnake1
2673 {
2674  rgbColor objColors[4] = {Colors::red*0.75, Colors::blue*0.75, Colors::green*0.75, Colors::yellow*0.75};
2675  Graphics::point p = GetCenter(x, y);
2676  float radius = GetRadius()*0.95;
2677  switch (o)
2678  {
2679  case kBlock1:
2680  {
2681  radius = GetRadius();
2682  Graphics::point p = GetCenter(x, y);
2683  display.FrameSquare(p, radius-radius*0.3, objColors[0], radius*0.6);
2684  }
2685  break;
2686  case kBlock2:
2687  {
2688  radius = GetRadius();
2689  Graphics::point p = GetCenter(x, y);
2690  display.FrameSquare(p, radius-radius*0.3, objColors[1], radius*0.6);
2691  }
2692  break;
2693  case kBlock3:
2694  {
2695  radius = GetRadius();
2696  Graphics::point p = GetCenter(x, y);
2697  display.FrameSquare(p, radius-radius*0.3, objColors[2], radius*0.6);
2698  }
2699  break;
2700  case kBlock4:
2701  {
2702  radius = GetRadius();
2703  Graphics::point p = GetCenter(x, y);
2704  display.FrameSquare(p, radius-radius*0.3, objColors[3], radius*0.6);
2705  }
2706  break;
2707  case kEmpty:
2708  display.FillSquare(p, GetRadius(), rgbColor::mix(Colors::cyan, Colors::lightblue, 0.5)); break;
2709  break;
2710  case kGround:
2711  display.FillSquare(p, GetRadius(), Colors::brown+Colors::white*0.75); break;
2712  break;
2713  case kSpikes:
2714  if (y != height-1)
2715  {
2716 // display.FillNGon(p, radius, 3, 0, Colors::darkgray);
2717 // display.FillNGon(p, radius, 3, 60, Colors::gray*0.75f);
2718  display.FillSquare({p.x, p.y+radius/2.0f}, radius/2.0f, Colors::darkgray);
2719  display.FillTriangle({p.x-radius/2.0f, p.y}, {p.x-radius/4.0f, p.y-radius/2}, {p.x, p.y}, Colors::darkred);
2720  display.FillTriangle({p.x+radius/2.0f, p.y}, {p.x+radius/4.0f, p.y-radius/2}, {p.x, p.y}, Colors::darkred);
2721 
2722  display.FillTriangle({p.x-radius/2.0f, p.y}, {p.x-radius, p.y+radius/4}, {p.x-radius/2.0f, p.y+radius/2}, Colors::darkred);
2723  display.FillTriangle({p.x-radius/2.0f, p.y+radius/2}, {p.x-radius, p.y+3*radius/4}, {p.x-radius/2.0f, p.y+radius}, Colors::darkred);
2724 
2725  display.FillTriangle({p.x+radius/2.0f, p.y}, {p.x+radius, p.y+radius/4}, {p.x+radius/2.0f, p.y+radius/2}, Colors::darkred);
2726  display.FillTriangle({p.x+radius/2.0f, p.y+radius/2}, {p.x+radius, p.y+3*radius/4}, {p.x+radius/2.0f, p.y+radius}, Colors::darkred);
2727  }
2728  break;
2729  case kPortal1:
2730  case kPortal2:
2731  {
2732  double radius = GetRadius()*0.95;
2733  double offset1 = (sin(time*1.0)); // -1...+1
2734  double offset2 = (sin(time*1.5)); // -1...+1
2735  double offset3 = (sin(time*2.0)); // -1...+1
2736  double offset4 = (sin(time*2.5)); // -1...+1
2737  display.FillCircle(p, radius+offset1*radius*0.25, Colors::red);
2738  display.FillCircle(p, radius*0.75+offset2*radius*0.2, Colors::blue);
2739  display.FillCircle(p, radius*0.5+offset3*radius*0.15, Colors::green);
2740  display.FillCircle(p, radius*0.25+offset4*radius*0.1, Colors::purple);
2741  }
2742  break;
2743  case kFruit:
2744  {
2745  const float rows = 5;
2746  const float counts[] = {3, 4, 3, 2, 1};
2747  float grapeRadius = GetRadius()*0.15f;
2748  for (int t = 0; t < rows; t++)
2749  {
2750  for (int v = 0; v < counts[t]; v++)
2751  {
2752  Graphics::point tmp = p;
2753  tmp.y -= rows*grapeRadius;
2754  tmp.y += 2*t*grapeRadius;
2755  tmp.x = tmp.x-grapeRadius*(counts[t]-1)+v*grapeRadius*2;
2756  tmp.x += sin(time*1.1+(t+v)*0.1)*GetRadius()*0.1;
2757  tmp.y += cos(time*0.95+(t+v)*0.1)*GetRadius()*0.1;
2758  display.FillCircle(tmp, grapeRadius*1.1, Colors::purple*sin(time*1.95+v+t));
2759  }
2760  }
2761  break;
2762  }
2763  case kExit:
2764  {
2765  double radius = GetRadius()*0.95;
2766  double localTime = time;
2767  localTime /= 4.0;
2768  double offset1 = (sin(localTime*1.0))*180; // -1...+1
2769  double offset2 = (sin(localTime*1.5))*180; // -1...+1
2770  double offset3 = (sin(localTime*2.0))*180; // -1...+1
2771  display.FillNGon(p, radius, 5, 0+offset1, Colors::yellow);
2772  display.FillNGon(p, radius*0.66, 5, 36+offset2, Colors::orange);
2773  display.FillNGon(p, radius*0.25, 5, 54+offset3, Colors::red);
2774  break;
2775  }
2776  case kSnake1:
2777  {
2778  int snake = 0;
2779  rgbColor color = Colors::red;
2780  float smallRadius = 0.75*GetRadius();
2781  Graphics::rect r(p, GetRadius());
2782  r.top+=smallRadius;
2783  r.bottom-=smallRadius;
2784  display.FillRect(r, color);
2785  r.top-=smallRadius;
2786  r.bottom+=smallRadius;
2787  r.left+=smallRadius;
2788  r.right-=smallRadius;
2789  display.FillRect(r, color);
2790 
2791  display.FillCircle(p+Graphics::point(smallRadius-GetRadius(), smallRadius-GetRadius()), smallRadius, color);
2792  display.FillCircle(p+Graphics::point(smallRadius-GetRadius(), -smallRadius+GetRadius()), smallRadius, color);
2793  display.FillSquare(p+Graphics::point(-GetRadius()/2+GetRadius(), GetRadius()/2-GetRadius()), GetRadius()/2, color);
2794  display.FillSquare(p+Graphics::point(-GetRadius()/2+GetRadius(), -GetRadius()/2+GetRadius()), GetRadius()/2, color);
2795 
2796  float eyescale = 1+snake/5.0;
2797  rgbColor tmp = color*0.5;
2798  tmp = Colors::white;
2799  // draw both eyes
2800  p.x+=GetRadius()*0.2;
2801  display.FillCircle(p, GetRadius()*0.2*eyescale, tmp);
2802  p.x-=2*GetRadius()*0.2;
2803  display.FillCircle(p, GetRadius()*0.2*eyescale, tmp);
2804  p.x+=2*GetRadius()*0.2;
2805 
2806  // draw iris
2807  display.FillCircle(p, GetRadius()*0.1*eyescale, Colors::black);
2808  p.x-=2*GetRadius()*0.2;
2809  display.FillCircle(p, GetRadius()*0.1*eyescale, Colors::black);
2810 
2811  p.x+=GetRadius()*0.2;
2812  p.y+=2*GetRadius()*0.25;
2813  display.FillNGon(p, GetRadius()*0.3, 3, 240, Colors::orange);
2814  break;
2815  }
2816  case kSnake2:
2817  {
2818  int snake = 1;
2819  rgbColor color = Colors::blue;
2820  float smallRadius = 0.75*GetRadius();
2821  Graphics::rect r(p, GetRadius());
2822  r.top+=smallRadius;
2823  r.bottom-=smallRadius;
2824  display.FillRect(r, color);
2825  r.top-=smallRadius;
2826  r.bottom+=smallRadius;
2827  r.left+=smallRadius;
2828  r.right-=smallRadius;
2829  display.FillRect(r, color);
2830 
2831  display.FillCircle(p+Graphics::point(smallRadius-GetRadius(), smallRadius-GetRadius()), smallRadius, color);
2832  display.FillCircle(p+Graphics::point(smallRadius-GetRadius(), -smallRadius+GetRadius()), smallRadius, color);
2833  display.FillSquare(p+Graphics::point(-GetRadius()/2+GetRadius(), GetRadius()/2-GetRadius()), GetRadius()/2, color);
2834  display.FillSquare(p+Graphics::point(-GetRadius()/2+GetRadius(), -GetRadius()/2+GetRadius()), GetRadius()/2, color);
2835 
2836  float eyescale = 1+snake/5.0;
2837  rgbColor tmp = color*0.5;
2838  tmp = Colors::white;
2839  // draw both eyes
2840  p.x+=GetRadius()*0.2;
2841  display.FillCircle(p, GetRadius()*0.2*eyescale, tmp);
2842  p.x-=2*GetRadius()*0.2;
2843  display.FillCircle(p, GetRadius()*0.2*eyescale, tmp);
2844  p.x+=2*GetRadius()*0.2;
2845 
2846  // draw iris
2847  display.FillCircle(p, GetRadius()*0.1*eyescale, Colors::black);
2848  p.x-=2*GetRadius()*0.2;
2849  display.FillCircle(p, GetRadius()*0.1*eyescale, Colors::black);
2850 
2851  p.x+=GetRadius()*0.2;
2852  p.y+=2*GetRadius()*0.25;
2853  display.FillNGon(p, GetRadius()*0.3, 3, 240, Colors::orange);
2854  break;
2855  }
2856  case kNothing:
2857  default: break;
2858  }
2859 }
2860 
2861 void SnakeBird::Draw(Graphics::Display &display, double time) const
2862 {
2863  for (int x = -2; x <= width+1; x++)
2864  {
2865  auto p = GetCenter(x, height-1);
2866  p.y += GetRadius()+0.25*GetRadius()*sin(x*4.0/width+time*0.43);
2867  p.x += fmod(time*0.25, 2)*GetRadius();
2868  display.FillNGon(p, GetRadius(), 3, 180, Colors::darkblue+Colors::darkred);
2869  }
2870 
2871  if (portal1Loc != -1)
2872  {
2873  double radius = GetRadius()*0.95;
2874  Graphics::point p = GetCenter(GetX(portal1Loc), GetY(portal1Loc));
2875  double offset1 = (sin(time*1.0)); // -1...+1
2876  double offset2 = (sin(time*1.5)); // -1...+1
2877  double offset3 = (sin(time*2.0)); // -1...+1
2878  double offset4 = (sin(time*2.5)); // -1...+1
2879  display.FillCircle(p, radius+offset1*radius*0.25, Colors::red);
2880  display.FillCircle(p, radius*0.75+offset2*radius*0.2, Colors::blue);
2881  display.FillCircle(p, radius*0.5+offset3*radius*0.15, Colors::green);
2882  display.FillCircle(p, radius*0.25+offset4*radius*0.1, Colors::purple);
2883  }
2884  if (portal2Loc != -1)
2885  {
2886  double radius = GetRadius()*0.95;
2887  Graphics::point p = GetCenter(GetX(portal2Loc), GetY(portal2Loc));
2888  double offset1 = (sin(time*1.0+0.1)); // -1...+1
2889  double offset2 = (sin(time*1.5+0.1)); // -1...+1
2890  double offset3 = (sin(time*2.0+0.1)); // -1...+1
2891  double offset4 = (sin(time*2.5+0.1)); // -1...+1
2892  display.FillCircle(p, radius+offset1*radius*0.25, Colors::red);
2893  display.FillCircle(p, radius*0.75+offset2*radius*0.2, Colors::blue);
2894  display.FillCircle(p, radius*0.5+offset3*radius*0.15, Colors::green);
2895  display.FillCircle(p, radius*0.25+offset4*radius*0.1, Colors::purple);
2896  }
2897 
2898 }
2899 
2900 bool SnakeBird::GetPointFromCoordinate(Graphics::point p, int &x, int &y)
2901 {
2902  x = (p.x+1.0-GetRadius())/(2.0*GetRadius())+0.5;
2903  y = (p.y+1.0-GetRadius())/(2.0*GetRadius())+0.5;
2904  if (x >= 0 && x < width && y >= 0 && y < height)
2905  return true;
2906  return false;
2907 }
2908 
2909 Graphics::point SnakeBird::GetCenter(int x, int y) const
2910 {
2911  Graphics::point p;
2912  p.x = -1+2*x*GetRadius()+GetRadius();
2913  p.y = -1+2*y*GetRadius()+GetRadius();
2914  return p;
2915 }
2916 
2917 float SnakeBird::GetRadius() const
2918 {
2919  return 2.0/(std::max(width, height)*2.0);
2920 }
2921 
2922 void SnakeBird::Draw(Graphics::Display &display, const SnakeBirdState&s) const
2923 {
2924  Draw(display, s, -1);
2925 }
2926 
2927 void SnakeBird::Draw(Graphics::Display &display, const SnakeBirdState&s, int active) const
2928 {
2929  Draw(display, s, active, 0);
2930 }
2931 
2932 void SnakeBird::DrawSnakeEnteringGoal(Graphics::Display &display,
2933  const SnakeBirdState &s,
2934  int snake, bool isActive, double percentComplete) const
2935 {
2936  const rgbColor snakeColors[4] = {Colors::red, Colors::blue, Colors::green, Colors::yellow};
2937  int index = s.GetSnakeHeadLoc(snake);
2938 
2939  int x = GetX(index);
2940  int y = GetY(index);
2941  int len = s.GetSnakeLength(snake);
2942  if (len < 0)
2943  return;
2944  float timePerSegment = 1.0f/len;
2945  float drawnTime = 0;
2946  int nextIn = percentComplete*len;
2947  int cnt = 0;
2948 
2949  int prevx = x, prevy = y; // 2 steps back
2950  for (int t = nextIn; t < len; t++)
2951  {
2952  int lastx = x;
2953  int lasty = y;
2954  switch (s.GetSnakeDir(snake, cnt))
2955  {
2956  case kUp: y-=1; break;
2957  case kDown: y+=1; break;
2958  case kRight: x+=1; break;
2959  case kLeft: x-=1; break;
2960  default: break;
2961  }
2962  bool tail = (t+1==len);
2963 
2964  Graphics::point p = GetCenter(lastx, lasty);
2965  Graphics::point p2 = GetCenter(prevx, prevy);
2966 // rgbColor color = snakeColors[snake]*(((++cnt+len)&1)?0.8:1.0);
2967  rgbColor color = snakeColors[snake]*(((lastx+lasty)&1)?0.8:1.0);
2968  ++cnt;
2969  prevx = lastx;
2970  prevy = lasty;
2971  if (t == len-1)
2972  {
2973  float perc = percentComplete*len-nextIn;
2974  p = p2*perc+p*(1-perc);
2975  }
2976 
2977  DrawSnakeSegment(display, p, color,
2978  (t == nextIn), // head - (was false always?)
2979  tail, // tail
2980  false, // awake
2981  (t == nextIn)?kUp:s.GetSnakeDir(snake, cnt-2), // dirFrom
2982  tail?kUp:s.GetSnakeDir(snake, cnt-1), snake, false); //dirTo - ignored if tail is true
2983  }
2984 }
2985 
2986 
2987 void SnakeBird::DrawTranslatingSnake(Graphics::Display &display, const SnakeBirdState &old, const SnakeBirdState &s,
2988  int snake, bool isActive, double percentComplete) const
2989 {
2990  const rgbColor snakeColors[4] = {Colors::red, Colors::blue, Colors::green, Colors::yellow};
2991  int index = s.GetSnakeHeadLoc(snake);
2992 
2993  int x = GetX(index);
2994  int y = GetY(index);
2995  int deltax = GetX(old.GetSnakeHeadLoc(snake))-x;
2996  int deltay = GetY(old.GetSnakeHeadLoc(snake))-y;
2997  // display.FillSquare(p, GetRadius(), Colors::red);
2998  int len = s.GetSnakeBodyEnd(snake)-s.GetSnakeBodyEnd(snake-1);
2999  int oldLen = old.GetSnakeBodyEnd(snake)-old.GetSnakeBodyEnd(snake-1);
3000  bool deadSegment = IsOnSpikes(s, snake);//GetGroundType(deltax+x, deltay+y)==kSpikes;
3001 
3002  int cnt = 0;
3003  for (int t = 0; t < len; t++)
3004  {
3005  switch (s.GetSnakeDir(snake, t))
3006  {
3007  case kUp: y-=1; break;
3008  case kDown: y+=1; break;
3009  case kRight: x+=1; break;
3010  case kLeft: x-=1; break;
3011  default: break;
3012  }
3013  bool tail = t+1==(s.GetSnakeBodyEnd(snake)-s.GetSnakeBodyEnd(snake-1));
3014  Graphics::point p = GetCenter(x, y);
3015  Graphics::point p2 = GetCenter(deltax+x, deltay+y);
3016  rgbColor color = snakeColors[snake]*(((++cnt+len)&1)?0.8:1.0);
3017 // rgbColor color = snakeColors[snake]*(((x+y)&1)?0.8:1.0);
3018 // ++cnt;
3019 
3020  if (oldLen == len)
3021  {
3022  p = p*percentComplete + p2*(1-percentComplete);
3023  }
3024  else if (tail)
3025  {
3026  p = p2;
3027  }
3028  DrawSnakeSegment(display, p, color,
3029  false, // head
3030  tail, // tail
3031  false, // awake
3032  s.GetSnakeDir(snake, t), // dirFrom
3033  tail?kUp:s.GetSnakeDir(snake, t+1), snake, deadSegment); //dirTo - ignored if tail is true
3034  }
3035 
3036  // Now draw head
3037  index = s.GetSnakeHeadLoc(snake);
3038 
3039  x = GetX(index);
3040  y = GetY(index);
3041  deltax = GetX(old.GetSnakeHeadLoc(snake))-x;
3042  deltay = GetY(old.GetSnakeHeadLoc(snake))-y;
3043  // display.FillSquare(p, GetRadius(), Colors::red);
3044 
3045  Graphics::point p = GetCenter(x, y);
3046  Graphics::point p2 = GetCenter(deltax+x, deltay+y);
3047  p = p*percentComplete + p2*(1-percentComplete);
3048 // rgbColor color = snakeColors[snake]*(((deltax+deltay+x+y)&1)?0.8:1.0);
3049  rgbColor color = snakeColors[snake]*((len%2)?0.8:1.0);
3050  //bool deadSegment = GetGroundType(deltax+x, deltay+y)==kSpikes;
3051  DrawSnakeSegment(display, p, color, true, false, isActive, kUp, s.GetSnakeDir(snake, 0), snake, deadSegment);
3052 }
3053 
3054 void SnakeBird::DrawMovingSnake(Graphics::Display &display, const SnakeBirdState &old,
3055  const SnakeBirdState &s,
3056  int snake, bool isActive, double percentComplete) const
3057 {
3058  const rgbColor snakeColors[4] = {Colors::red, Colors::blue, Colors::green, Colors::yellow};
3059 
3060  int index = old.GetSnakeHeadLoc(snake);
3061  int x = GetX(index);
3062  int y = GetY(index);
3063  int deltax = GetX(old.GetSnakeHeadLoc(snake))-GetX(s.GetSnakeHeadLoc(snake));
3064  int deltay = GetY(old.GetSnakeHeadLoc(snake))-GetY(s.GetSnakeHeadLoc(snake));
3065  int lastx, lasty;
3066  bool deadSegment = IsOnSpikes(s, snake);//GetGroundType(deltax+x, deltay+y)==kSpikes;
3067 
3068  int len = old.GetSnakeLength(snake);
3069  bool ateFruit = (old.GetSnakeLength(snake) != s.GetSnakeLength(snake));
3070  int cnt = ateFruit?0:1;
3071  // 1. draw the entire old snake statically except for the tail
3072  snakeDir fromDir, toDir;
3073  fromDir = s.GetSnakeDir(snake, 0);
3074  toDir = old.GetSnakeDir(snake, 0);
3075  for (int t = 0; t < len-1; t++)
3076  {
3077  bool head = false;
3078  // cause the second segment to be rounded until the new head moves
3079  if (percentComplete < 0.1 && t == 0)
3080  head = true;
3081  rgbColor color = snakeColors[snake]*(((++cnt+len)&1)?0.8:1.0);
3082 // rgbColor color = snakeColors[snake]*(((x+y)&1)?0.8:1.0);
3083 // ++cnt;
3084  Graphics::point p = GetCenter(x, y);
3085  //bool deadSegment = GetGroundType(x, y)==kSpikes;
3086  DrawSnakeSegment(display, p, color,
3087  head, // head
3088  false, // tail
3089  false, // awake
3090  fromDir, // dirFrom
3091  toDir, snake, deadSegment); //dirTo - ignored if tail is true
3092 
3093  fromDir = old.GetSnakeDir(snake, t);
3094  toDir = old.GetSnakeDir(snake, t+1);
3095 
3096  lastx = x; lasty = y;
3097  switch (old.GetSnakeDir(snake, t))
3098  {
3099  case kUp: y-=1; break;
3100  case kDown: y+=1; break;
3101  case kRight: x+=1; break;
3102  case kLeft: x-=1; break;
3103  default: break;
3104  }
3105  }
3106 
3107  // Now draw moving tail - x/y are at the end of the old snake
3108  if (!ateFruit)
3109  {
3110  Graphics::point p = GetCenter(lastx, lasty);
3111  Graphics::point p2 = GetCenter(x, y);
3112  p = p*percentComplete + p2*(1-percentComplete);
3113  color = snakeColors[snake];//*(((len)%2)?0.8:1.0);
3114 // color = snakeColors[snake]*(((x+y)&1)?0.8:1.0);
3115  //bool deadSegment = GetGroundType(x, y)==kSpikes;
3116  DrawSnakeSegment(display, p, color, false, true, false, old.GetSnakeDir(snake, len-2), kUp/*next*/, snake, deadSegment);
3117  }
3118  else {
3119  Graphics::point p = GetCenter(x, y);
3120  rgbColor color = snakeColors[snake]*(((++cnt+len)&1)?0.8:1.0);
3121 // ++cnt;
3122 // rgbColor color = snakeColors[snake]*(((x+y)&1)?0.8:1.0);
3123  bool deadSegment = GetGroundType(x, y)==kSpikes;
3124  DrawSnakeSegment(display, p, color, false, true, false, old.GetSnakeDir(snake, len-2), kUp/*next*/, snake, deadSegment);
3125  }
3126 
3127  // Now draw moving head
3128  index = s.GetSnakeHeadLoc(snake);
3129 
3130  x = GetX(index);
3131  y = GetY(index);
3132 // int deltax = GetX(old.GetSnakeHeadLoc(snake))-x;
3133 // int deltay = GetY(old.GetSnakeHeadLoc(snake))-y;
3134  Graphics::point p = GetCenter(x, y);
3135  Graphics::point p2 = GetCenter(deltax+x, deltay+y);
3136  p = p*percentComplete + p2*(1-percentComplete);
3137  rgbColor color = snakeColors[snake]*((len%2)?1.0:0.8);
3138 // rgbColor color = snakeColors[snake]*(((deltax+deltay+x+y)&1)?0.8:1.0);
3139  //bool deadSegment = GetGroundType(deltax+x, deltay+y)==kSpikes;
3140  DrawSnakeSegment(display, p, color, true, false, isActive, kUp, s.GetSnakeDir(snake, 0), snake, deadSegment);
3141 }
3142 
3143 
3144 void SnakeBird::Draw(Graphics::Display &display,
3145  const SnakeBirdState &old,
3146  const SnakeBirdState &s,
3147  int active, double percentComplete, double globalTime) const
3148 {
3150  rgbColor objColors[4] = {Colors::red*0.75, Colors::blue*0.75, Colors::green*0.75, Colors::yellow*0.75};
3151 
3152  // draw objects - connections wil be behind snakes
3153  static std::vector<Graphics::point> points;
3154  for (int x = 0; x < 4; x++)
3155  {
3156  double radius = GetRadius();//*0.95;
3157  if (editing == true)
3158  { printf("WARNING: editing not turned off.\n"); }
3159  points.clear();
3160  if (s.GetObjectLocation(x) == kDead)
3161  continue;
3162  for (int i = 0; i < objects[x].size(); i++)
3163  {
3164  Graphics::point p = GetCenter(GetX(objects[x][i])+GetX(s.GetObjectLocation(x)),
3165  GetY(objects[x][i])+GetY(s.GetObjectLocation(x)));
3166  Graphics::point p2 = GetCenter(GetX(objects[x][i])+GetX(old.GetObjectLocation(x)),
3167  GetY(objects[x][i])+GetY(old.GetObjectLocation(x)));
3168  p = p*percentComplete+p2*(1-percentComplete);
3169  display.FrameSquare(p, radius-radius*0.3, objColors[x], radius*0.6);
3170  if (!objectFullyConnected[x])
3171  points.push_back(p);
3172  }
3173  if (points.size() > 0)
3174  {
3175  for (int z = 0; z < points.size(); z++)
3176  {
3177  for (int y = z+1; y < points.size(); y++)
3178  {
3179  if (fequal(points[z].x, points[y].x))
3180  {
3181  Graphics::rect r(points[z].x-GetRadius()*0.2f,
3182  points[z].y+GetRadius()-radius*0.3*2,
3183  points[y].x+GetRadius()*0.2f,
3184  points[y].y-GetRadius()+radius*0.3*2);
3185  display.FillRect(r, objColors[x]*0.5);
3186  }
3187  else if (fequal(points[z].y, points[y].y))
3188  {
3189  Graphics::rect r(points[z].x+GetRadius()-radius*0.3*2,
3190  points[z].y-GetRadius()*0.2f,
3191  points[y].x-GetRadius()+radius*0.3*2,
3192  points[y].y+GetRadius()*0.2f);
3193  display.FillRect(r, objColors[x]*0.5);
3194  }
3195  }
3196  }
3197 // display.DrawLineSegments(points, GetRadius()*0.5, objColors[x]*0.8);
3198  }
3199  }
3200 
3201 
3202  // draw bodies
3203  for (int snake = 0; snake < s.GetNumSnakes(); snake++)
3204  {
3205  // get head loc
3206  int index = s.GetSnakeHeadLoc(snake);
3207 // if (index == kInGoal)
3208 // continue;
3209  if (index == kInGoal && old.GetSnakeHeadLoc(snake) == kInGoal)
3210  {
3211  // do nothing
3212  }
3213  else if (index != kInGoal && old.GetSnakeHeadLoc(snake) == kInGoal) // undoing an action
3214  {
3215  DrawTranslatingSnake(display, s, s, snake, (active==-1)||(active==snake), percentComplete);
3216  }
3217  else if (index == kInGoal && old.GetSnakeHeadLoc(snake) != kInGoal) // moved into goal
3218  {
3219  DrawSnakeEnteringGoal(display, old, snake, (active==-1)||(active==snake), percentComplete);
3220  }
3221  else if (s.GetSnakeLength(snake) != old.GetSnakeLength(snake))
3222  {
3223  // body stays the same - just draw head moving
3224  DrawMovingSnake(display, old, s, snake, (active==-1)||(active==snake), percentComplete);
3225  continue;
3226  }
3227  else if (s.GetBodyBits(snake) == old.GetBodyBits(snake))
3228  {
3229  // tween entire snake
3230  DrawTranslatingSnake(display, old, s, snake, (active==-1)||(active==snake), percentComplete);
3231  continue;
3232  }
3233  else {
3234  // whole snake moves -
3235  DrawMovingSnake(display, old, s, snake, (active==-1)||(active==snake), percentComplete);
3236  continue;
3237  }
3238  }
3239 
3240  for (int x = 0; x < fruit.size(); x++)
3241  {
3242  if (s.GetFruitPresent(x))
3243  {
3244  const float rows = 5;
3245  const float counts[] = {3, 4, 3, 2, 1};
3246  Graphics::point p = GetCenter(GetX(fruit[x]), GetY(fruit[x]));
3247  float grapeRadius = GetRadius()*0.15f;
3248  for (int t = 0; t < rows; t++)
3249  {
3250  for (int v = 0; v < counts[t]; v++)
3251  {
3252  Graphics::point tmp = p;
3253  tmp.y -= rows*grapeRadius;
3254  tmp.y += 2*t*grapeRadius;
3255  tmp.x = tmp.x-grapeRadius*(counts[t]-1)+v*grapeRadius*2;
3256  tmp.x += sin(globalTime*1.1+(t+v)*0.1)*GetRadius()*0.1;
3257  tmp.y += cos(globalTime*0.95+(t+v)*0.1)*GetRadius()*0.1;
3258  display.FillCircle(tmp, grapeRadius*1.1, Colors::purple*sin(globalTime*1.95+v+t));
3259  }
3260  }
3261  }
3262  }
3263 
3264  // draw exit
3265  if (exitLoc != -1)
3266  {
3267  Graphics::point p = GetCenter(GetX(exitLoc), GetY(exitLoc));
3268  double radius = GetRadius()*0.95;
3269  double time = globalTime;
3270  if (s.KFruitEaten(fruit.size()))
3271  {
3272  radius = radius+0.25*radius*(1+sin(globalTime*2.5));
3273  time *= 2.0;
3274  }
3275  else {
3276  time /= 4.0;
3277  }
3278  double offset1 = (sin(time*1.0))*180; // -1...+1
3279  double offset2 = (sin(time*1.5))*180; // -1...+1
3280  double offset3 = (sin(time*2.0))*180; // -1...+1
3281  display.FillNGon(p, radius, 5, 0+offset1, Colors::yellow);
3282  display.FillNGon(p, radius*0.66, 5, 36+offset2, Colors::orange);
3283  display.FillNGon(p, radius*0.25, 5, 54+offset3, Colors::red);
3284  }
3285 
3286 }
3287 
3288 void SnakeBird::Draw(Graphics::Display &display, const SnakeBirdState&s, int active, double globalTime) const
3289 {
3290  Draw(display, s, s, active, 0, globalTime);
3291 }
3292 
3293 void SnakeBird::DrawSnakeSegment(Graphics::Display &display, Graphics::point p, const rgbColor &c, bool head, bool tail, bool awake, snakeDir dirFrom, snakeDir dirTo, int snake, bool isDead) const
3294 {
3295 // const float cornerWidth = 0.75;
3296 // float offset = cornerWidth*GetRadius();
3297  rgbColor color = c;
3298  if (isDead)
3299  color.mix(Colors::black, 0.75);
3300  float smallRadius = 0.75*GetRadius();
3301  Graphics::rect r(p, GetRadius());
3302  r.top+=smallRadius;
3303  r.bottom-=smallRadius;
3304  display.FillRect(r, color);
3305  r.top-=smallRadius;
3306  r.bottom+=smallRadius;
3307  r.left+=smallRadius;
3308  r.right-=smallRadius;
3309  display.FillRect(r, color);
3310 
3311  if ((!head && (dirFrom == kDown || dirFrom == kRight)) ||
3312  (!tail && (dirTo == kUp || dirTo == kLeft)))
3313  {
3314  display.FillSquare(p+Graphics::point(GetRadius()/2-GetRadius(), GetRadius()/2-GetRadius()), GetRadius()/2, color);
3315  }
3316  else {
3317  display.FillCircle(p+Graphics::point(smallRadius-GetRadius(), smallRadius-GetRadius()), smallRadius, color);
3318  }
3319 
3320  if ((!head && (dirFrom == kUp || dirFrom == kRight)) ||
3321  (!tail && (dirTo == kDown || dirTo == kLeft)))
3322  {
3323  display.FillSquare(p+Graphics::point(GetRadius()/2-GetRadius(), -GetRadius()/2+GetRadius()), GetRadius()/2, color);
3324  }
3325  else {
3326  display.FillCircle(p+Graphics::point(smallRadius-GetRadius(), -smallRadius+GetRadius()), smallRadius, color);
3327  }
3328 
3329  if ((!head && (dirFrom == kDown || dirFrom == kLeft)) ||
3330  (!tail && (dirTo == kUp || dirTo == kRight)))
3331  {
3332  display.FillSquare(p+Graphics::point(-GetRadius()/2+GetRadius(), GetRadius()/2-GetRadius()), GetRadius()/2, color);
3333  }
3334  else {
3335  display.FillCircle(p+Graphics::point(-smallRadius+GetRadius(), smallRadius-GetRadius()), smallRadius, color);
3336  }
3337 
3338  if ((!head && (dirFrom == kUp || dirFrom == kLeft)) ||
3339  (!tail && (dirTo == kDown || dirTo == kRight)))
3340  {
3341  display.FillSquare(p+Graphics::point(-GetRadius()/2+GetRadius(), -GetRadius()/2+GetRadius()), GetRadius()/2, color);
3342  }
3343  else {
3344  display.FillCircle(p+Graphics::point(-smallRadius+GetRadius(), -smallRadius+GetRadius()), smallRadius, color);
3345  }
3346 
3347  if (head)
3348  {
3349  float eyescale = 1+snake/5.0;
3350  rgbColor tmp = color*0.5;
3351  if (isDead)
3352  tmp = Colors::black;
3353  else if (awake)
3354  tmp = Colors::white;
3355  // draw both eyes
3356  p.x+=GetRadius()*0.2;
3357  display.FillCircle(p, GetRadius()*0.2*eyescale, tmp);
3358  p.x-=2*GetRadius()*0.2;
3359  display.FillCircle(p, GetRadius()*0.2*eyescale, tmp);
3360  p.x+=2*GetRadius()*0.2;
3361 
3362  // draw iris
3363  if (awake)
3364  display.FillCircle(p, GetRadius()*0.1*eyescale, Colors::black);
3365  p.x-=2*GetRadius()*0.2;
3366  //display.FillCircle(p, GetRadius()*0.2*eyescale, awake?Colors::white:(color*0.5));
3367  if (awake)
3368  display.FillCircle(p, GetRadius()*0.1*eyescale, Colors::black);
3369 
3370  p.x+=GetRadius()*0.2;
3371  p.y+=2*GetRadius()*0.25;
3372  display.FillNGon(p, GetRadius()*0.3, 3, 240, Colors::orange);
3373  }
3374 
3375 
3376  // in spikes!
3377 // if (world[GetIndex(x, y)] == kSpikes)
3378 // {
3379 // display.FillNGon(p, GetRadius()*0.95, 3, 0, Colors::darkgray);
3380 // display.FillNGon(p, GetRadius()*0.95, 3, 60, Colors::gray*0.75f);
3381 // return;
3382 // }
3383 }
3384 
3385 void SnakeBird::DrawLabel(Graphics::Display &display, int x, int y, const char *str)
3386 {
3387  display.DrawText(str, GetCenter(x, y), GetColor(), GetRadius()*1.5, Graphics::textAlignLeft, Graphics::textBaselineMiddle);
3388 }
3389 
3390 void SnakeBird::DrawSmallLabel(Graphics::Display &display, int x, int y, const char *str)
3391 {
3392  display.DrawText(str, GetCenter(x, y)+Graphics::point(GetRadius()/2, GetRadius()/2), GetColor(), 0.75*GetRadius(), Graphics::textAlignCenter, Graphics::textBaselineMiddle);
3393 }
3394 
3395 
3396 void SnakeBird::DrawLine(Graphics::Display &display, const SnakeBirdState &x, const SnakeBirdState &y, float width) const
3397 {
3398 
3399 }
3400 
3401 
3402 }
SnakeBird::SnakeBirdState::GetObjectLocation
int GetObjectLocation(int whichObstacle) const
Definition: SnakeBird.h:162
SnakeBird::kPortal
@ kPortal
Definition: SnakeBird.h:252
SnakeBird::SnakeBird
Definition: SnakeBird.h:280
SnakeBird::kSnake4
@ kSnake4
Definition: SnakeBird.h:268
Graphics::point
Definition: Graphics.h:32
SnakeBird::kNoDirection
@ kNoDirection
Definition: SnakeBird.h:42
Graphics::point::y
float y
Definition: Graphics.h:36
Colors::GetColor
rgbColor GetColor(float v, float vmin, float vmax, int type)
Given min/max values, get a color from a color schema.
Definition: Colors.cpp:24
SnakeBird::SnakeBirdState::GetBodyBits
uint64_t GetBodyBits(int whichSnake) const
Definition: SnakeBird.h:123
SnakeBird::kNothing
@ kNothing
Definition: SnakeBird.h:271
rgbColor
A color; r/g/b are between 0...1.
Definition: Colors.h:17
SnakeBird::SnakeBirdState
Definition: SnakeBird.h:44
SnakeBird::kPortal2
@ kPortal2
Definition: SnakeBird.h:251
Graphics::Display::FillSquare
void FillSquare(point p, float radius, rgbColor c)
Definition: Graphics.cpp:92
min
double min(double a, double b)
Definition: FPUtil.h:35
SnakeBird::kNeedsInitialization
@ kNeedsInitialization
Definition: SnakeBird.h:211
SnakeBird::SnakeBirdState::ToggleFruitPresent
void ToggleFruitPresent(int which)
Definition: SnakeBird.h:168
Graphics::Display::FillTriangle
void FillTriangle(point p1, point p2, point p3, rgbColor c)
Definition: Graphics.cpp:146
SnakeBird::kDown
@ kDown
Definition: SnakeBird.h:42
SnakeBird::kLeft
@ kLeft
Definition: SnakeBird.h:42
SnakeBird::SnakeBirdState::GetSnakeLength
int GetSnakeLength(int whichSnake) const
Definition: SnakeBird.h:87
SnakeBird::kSnakeMask
const uint8_t kSnakeMask
Definition: SnakeBird.h:242
SnakeBird::SnakeBirdAnimationStep::anim
SnakeBirdAnimation anim
Definition: SnakeBird.h:217
SnakeBird::TeleportResult
TeleportResult
Definition: SnakeBird.h:274
SnakeBird::kSpikes
@ kSpikes
Definition: SnakeBird.h:256
Graphics::rect
Definition: Graphics.h:94
SnakeBird::kBlockMask
const uint8_t kBlockMask
Definition: SnakeBird.h:243
Colors::purple
const rgbColor purple
Definition: Colors.h:151
width
int width
Definition: SFML_HOG.cpp:54
Colors::darkblue
const rgbColor darkblue
Definition: Colors.h:143
Colors::lightblue
const rgbColor lightblue
Definition: Colors.h:144
SnakeBird::SnakeBirdAction::direction
unsigned int direction
Definition: SnakeBird.h:180
Graphics::Display::FillNGon
void FillNGon(point p, float radius, int sides, float rotation, rgbColor c)
Definition: Graphics.cpp:164
SnakeBird::SnakeBirdState::SetObjectLocation
void SetObjectLocation(int whichObstacle, int loc)
Definition: SnakeBird.h:163
SnakeBird::SnakeBirdState::SetSnakeHeadLoc
void SetSnakeHeadLoc(int whichSnake, int loc)
Definition: SnakeBird.h:62
SnakeBird::kMovement
@ kMovement
Definition: SnakeBird.h:203
SnakeBird
Definition: SnakeBird.cpp:14
SnakeBird::kNoTeleport
@ kNoTeleport
Definition: SnakeBird.h:275
Colors::brown
const rgbColor brown
Definition: Colors.h:132
Graphics::textBaselineMiddle
@ textBaselineMiddle
Definition: Graphics.h:27
SnakeBird::SnakeBirdState::SetSnakeBodyEnd
void SetSnakeBodyEnd(int whichSnake, int endOffset)
Definition: SnakeBird.h:67
SnakeBird::kExit
@ kExit
Definition: SnakeBird.h:249
SnakeBird::kPortal1
@ kPortal1
Definition: SnakeBird.h:250
kGround
@ kGround
Definition: Map.h:55
Graphics::rect::top
float top
Definition: Graphics.h:100
SnakeBird::SnakeBirdState::GetSnakeDir
snakeDir GetSnakeDir(int whichSnake, int segment) const
Definition: SnakeBird.h:90
SnakeBird::SnakeBirdAction::pushed
uint8_t pushed
Definition: SnakeBird.h:181
SnakeBird::kFall
@ kFall
Definition: SnakeBird.h:204
loc
Definition: MapGenerators.cpp:296
SnakeBird::kRight
@ kRight
Definition: SnakeBird.h:42
SnakeBird::SnakeBirdAnimationStep::a
SnakeBirdAction a
Definition: SnakeBird.h:218
Colors::black
const rgbColor black
Definition: Colors.h:119
SnakeBird::kPauseWhenDead
@ kPauseWhenDead
Definition: SnakeBird.h:210
GetX
int GetX(int loc)
Definition: Fling.h:67
SnakeBird::SnakeBirdWorldObject
SnakeBirdWorldObject
Definition: SnakeBird.h:245
SnakeBird::SnakeBirdState::InsertSnakeDir
void InsertSnakeDir(int whichSnake, snakeDir dir)
Definition: SnakeBird.h:98
SnakeBird::SnakeBirdAnimationStep::animationDuration
double animationDuration
Definition: SnakeBird.h:219
Graphics::Display
Definition: Graphics.h:146
Graphics::Display::FrameSquare
void FrameSquare(point p, float radius, rgbColor c, float lineWidth)
Definition: Graphics.cpp:82
SnakeBird::kFruit
@ kFruit
Definition: SnakeBird.h:248
SnakeBird::kTeleport
@ kTeleport
Definition: SnakeBird.h:206
Colors::darkgray
const rgbColor darkgray
Definition: Colors.h:123
SnakeBird::kSnake3
@ kSnake3
Definition: SnakeBird.h:267
SnakeBird::SnakeBirdAnimationStep
Definition: SnakeBird.h:214
SnakeBird::SnakeBirdAnimation
SnakeBirdAnimation
Definition: SnakeBird.h:202
SnakeBird::codeSize
const int codeSize
Definition: SnakeBird.h:39
SnakeBird::kBlock3
@ kBlock3
Definition: SnakeBird.h:261
Graphics::rect::right
float right
Definition: Graphics.h:100
Colors::cyan
const rgbColor cyan
Definition: Colors.h:153
SnakeBird::kInGoal
const int kInGoal
Definition: SnakeBird.h:36
SnakeBird::kWentInGoal
@ kWentInGoal
Definition: SnakeBird.h:207
SnakeBird::SnakeBirdState::GetNumSnakes
int GetNumSnakes() const
Definition: SnakeBird.h:54
SnakeBird::kEmpty
@ kEmpty
Definition: SnakeBird.h:247
height
int height
Definition: SFML_HOG.cpp:54
Colors::darkred
const rgbColor darkred
Definition: Colors.h:129
SnakeBird::SnakeBirdAnimationStep::teleportCount
int teleportCount
Definition: SnakeBird.h:220
SnakeBird::SnakeBirdState::KFruitEaten
bool KFruitEaten(int k) const
Definition: SnakeBird.h:169
SnakeBird::kUp
@ kUp
Definition: SnakeBird.h:42
SnakeBird::kSnake2
@ kSnake2
Definition: SnakeBird.h:266
max
#define max(a, b)
Definition: MinimalSectorAbstraction.cpp:40
SnakeBird::kTeleportToExit
@ kTeleportToExit
Definition: SnakeBird.h:277
SnakeBird::kTeleportSuccess
@ kTeleportSuccess
Definition: SnakeBird.h:276
Graphics::textAlignCenter
@ textAlignCenter
Definition: Graphics.h:20
SnakeBird::kSnake1
@ kSnake1
Definition: SnakeBird.h:265
SnakeBird::kDead
const int kDead
Definition: SnakeBird.h:35
SnakeBird::kGroundMask
const uint8_t kGroundMask
Definition: SnakeBird.h:241
SnakeBird::kInitialTeleport
@ kInitialTeleport
Definition: SnakeBird.h:205
SnakeBird::SnakeBirdState::InsertSnakeHeadDir
void InsertSnakeHeadDir(int whichSnake, snakeDir dir)
Definition: SnakeBird.h:111
SnakeBird::kFellInGoal
@ kFellInGoal
Definition: SnakeBird.h:208
SnakeBird::kBlock4
@ kBlock4
Definition: SnakeBird.h:262
Graphics::point::x
float x
Definition: Graphics.h:36
SnakeBird::kGround
@ kGround
Definition: SnakeBird.h:255
Colors::red
const rgbColor red
Definition: Colors.h:128
Colors::orange
const rgbColor orange
Definition: Colors.h:155
SnakeBird::SnakeBirdAction::bird
unsigned int bird
Definition: SnakeBird.h:179
SnakeBird::snakeDir
snakeDir
Definition: SnakeBird.h:41
Graphics::Display::FillCircle
void FillCircle(rect r, rgbColor c)
Definition: Graphics.cpp:128
Graphics::Display::FrameRect
void FrameRect(rect r, rgbColor c, float lineWidth)
Definition: Graphics.cpp:73
SnakeBird.h
SnakeBird::kBlock1
@ kBlock1
Definition: SnakeBird.h:259
Colors::darkgreen
const rgbColor darkgreen
Definition: Colors.h:136
Colors::yellow
const rgbColor yellow
Definition: Colors.h:148
Graphics::textAlignLeft
@ textAlignLeft
Definition: Graphics.h:21
Colors::gray
const rgbColor gray
Definition: Colors.h:121
Colors::white
const rgbColor white
Definition: Colors.h:120
Graphics::rect::left
float left
Definition: Graphics.h:100
Colors::blue
const rgbColor blue
Definition: Colors.h:142
SnakeBird::SnakeBirdState::GetFruitPresent
bool GetFruitPresent(int which) const
Definition: SnakeBird.h:167
SnakeBird::kCanEnterMask
const uint8_t kCanEnterMask
Definition: SnakeBird.h:240
Graphics::Display::DrawText
void DrawText(const char *text, point location, rgbColor c, float height, const char *typeface=0)
Definition: Graphics.cpp:221
SnakeBird::SnakeBirdAction
Definition: SnakeBird.h:177
SnakeBird::SnakeBirdState::GetSnakeHeadLoc
int GetSnakeHeadLoc(int whichSnake) const
Definition: SnakeBird.h:60
Graphics::rect::bottom
float bottom
Definition: Graphics.h:100
fequal
bool fequal(double a, double b, double tolerance=TOLERANCE)
Definition: FPUtil.h:32
Graphics::Display::FillRect
void FillRect(rect r, rgbColor c)
Definition: Graphics.cpp:101
Colors::green
const rgbColor green
Definition: Colors.h:135
SnakeBird::kDoneAnimation
@ kDoneAnimation
Definition: SnakeBird.h:209
GetY
int GetY(int loc)
Definition: Fling.h:68
SnakeBird::LoadSnake
std::vector< snakeDir > LoadSnake(std::vector< snakeDir > snakeBod, int width, int pos, std::vector< char > lvl)
Definition: SnakeBird.cpp:814
rgbColor::mix
static rgbColor mix(const rgbColor &c1, const rgbColor &c2, float perc)
Definition: Colors.h:25
SnakeBird::kBlock2
@ kBlock2
Definition: SnakeBird.h:260
SnakeBird::SnakeBirdState::GetSnakeBodyEnd
int GetSnakeBodyEnd(int whichSnake) const
Definition: SnakeBird.h:65