Fixed a typo
[runner_game.git] / runner_game.ino
blob4a6b4f9b6a9d2b30f3ab7af3230b1b87f30d6599
1 // Runner game by Johannes (dolphinana)
2 // Waived under CC0 1.0 (public domain) 
3 //
4 //
5 // Status: Finished
7 #include <LiquidCrystal.h>
8 LiquidCrystal lcd(12, 11, 2, 3, 4, 5);
10 #define BUTTON_RELEASED 0
11 #define BUTTON_JUST_PRESSED 1
12 #define BUTTON_PRESSED 2
13 #define BUTTON_PIN 8
15 #define BOX_MAX 8
16 #define BOX_CHAR '#'
18 // The amount of milliseconds you need to wait until you 
19 // can play again after game over. 
20 #define GAME_OVER_COOLDOWN_MS 1000
22 // 0 = released, 1 = pressed, 2 = held
23 unsigned char _buttonState;
25 #define GAME_STATE_PLAYING 0
26 #define GAME_STATE_MENU 1
27 #define GAME_STATE_GAMEOVER 2
28 unsigned char gameState;
30 unsigned long int frame;
31 unsigned long int timeSinceGameOverMs;
33 unsigned char reservedObstacles;
35 struct
37   int x;
38   int y;
39 } player;
42 #define SCREEN_TILE_WIDTH 16
43 #define SCREEN_TILE_HEIGHT 2
45 #define TILE_WIDTH 32
46 #define TILE_HEIGHT 2
47 unsigned char tiles[TILE_WIDTH*TILE_HEIGHT];
49 char removeObstacle(int tileX, int tileY)
51   const int tileIndex = tileY * TILE_WIDTH + tileX;
52   if (tileIndex != ' ')
53   {
54     tiles[tileY * TILE_WIDTH + tileX] = ' ';
55     reservedObstacles++;
56     return 1;
57   }
59   /* If the given position doesn't have the tile, then the function 
60      will return 0, which means not successful. */
61   return 0;
64 void handleButton()
66   if (digitalRead(BUTTON_PIN))
67   {
68     if (!_buttonState)
69       _buttonState = BUTTON_JUST_PRESSED;
70     else
71       _buttonState = BUTTON_PRESSED;
72   }
73   else 
74     _buttonState = BUTTON_RELEASED;
77 unsigned char isButtonPressed()
79   return _buttonState;
84 char spawnObstacle()
86   unsigned char success = 1;
88   unsigned char newX = random(SCREEN_TILE_WIDTH, TILE_WIDTH - 1);
89   unsigned char newY = random(TILE_HEIGHT);
90   unsigned char newIndex = newY * TILE_WIDTH + newX;
91   
92   if (tiles[newIndex] != ' ')
93   {
94     reservedObstacles++;
95     success = 0;
96   }
98   if (success);
99   {
100     tiles[newIndex] = BOX_CHAR;
101   }
103   return success;
106 void initPlaying()
108   for (int i = 0; i < TILE_WIDTH*TILE_HEIGHT; i++)
109   {
110     tiles[i] = ' ';
111   }
113   randomSeed(frame);
114   for (int i = 0; i < BOX_MAX; i++)
115   {
116     spawnObstacle();
117   }
119   gameState = GAME_STATE_PLAYING;
122 void setup() 
124   // Adding serial debuggin stuff makes the program 800 bytes larger
125 #if 0
126   Serial.begin(9600);
127   Serial.println("Gayme :)");
128   Serial.println();
129 #endif
131   pinMode(BUTTON_PIN, INPUT);
133   _buttonState = 0;
134   gameState = GAME_STATE_MENU;
135   frame = 0;
136   player.x = 0;
137   player.y = 0;
138   timeSinceGameOverMs = 0;
139   reservedObstacles = 0;
141   lcd.begin(16, 2);
142   lcd.clear();
143   lcd.setCursor(0,0);
145   pinMode(13, OUTPUT);
148 void loop() 
150   handleButton();
151   digitalWrite(13, LOW);
153   switch (gameState)
154   {
155   case GAME_STATE_GAMEOVER:
156     lcd.setCursor(3, 0);
157     lcd.print("Game  Over"); 
158     
159     if (millis() > timeSinceGameOverMs + GAME_OVER_COOLDOWN_MS)
160     {
161       lcd.setCursor(3, 1);
162       lcd.print("Try Again?"); 
164       if (isButtonPressed())
165         initPlaying();
166     }
167     break;
168   case GAME_STATE_MENU:
169     if (frame == 0)
170       lcd.print("Runner Game OwO");
173     if (isButtonPressed())
174     {
175       initPlaying();
176       lcd.clear();
177     }
178     break;
179   case GAME_STATE_PLAYING:
181     /*
182       The scope of the if statement below will be responsible for 
183       moving the obstacles towards left and also spawning obstacles
184       every time certain amount of time has passed.
185     */
186     if (frame % 8 == 1)
187     {
188       for (int y = 0; y < TILE_HEIGHT; y++)
189       {
190         for (int x = 0; x < TILE_WIDTH; x++)
191         {
192           if (tiles[y * TILE_WIDTH + x] == BOX_CHAR)
193           {
194             // Move the obstacles (box) to the left
195             tiles[y * TILE_WIDTH + x] = ' ';
197             if (x != 0)
198               tiles[y * TILE_WIDTH + x - 1] = BOX_CHAR;
199             else
200             {
201               spawnObstacle();       
202             }
203           }
204         }
205       }
207       /*
208         The scope of this if statement below will spawn reserved obstacles,
209         if there are any.
210       */
211       if (reservedObstacles > 0)
212       {
213         reservedObstacles--;
214         if (!spawnObstacle())
215         {
216         }
217       }
218     }
221     if (isButtonPressed() == BUTTON_JUST_PRESSED)
222     {
223       player.y = !player.y;
224     }
226     {
227       /* 
228          We can't check if there are two tiles next to each other at the 
229          end of the tile area because tiles might spawn at random positions 
230          outside of what the game displays.
231       */ 
232       const int x = SCREEN_TILE_WIDTH - 1;
234       /*  if the obstacles look like this  # 
235                                            # 
236       */
237       if (tiles[x] == BOX_CHAR && tiles[x + TILE_WIDTH] == BOX_CHAR)
238       {
239         removeObstacle(x, random(TILE_HEIGHT));
240       }
242       /*  if the obstacles look like this   # 
243                                            # 
244       */
245       if (tiles[0 * TILE_WIDTH + x] == BOX_CHAR && 
246             tiles[1 * TILE_WIDTH + x - 1] == BOX_CHAR)
247       {
248         removeObstacle(x, 0);
249       }
251       /*  if the obstacles look like this  # 
252                                             # 
253       */
254       if (tiles[0 * TILE_WIDTH + x - 1] == BOX_CHAR && 
255             tiles[1 * TILE_WIDTH + x] == BOX_CHAR)
256       {
257         removeObstacle(x, 1);
258       }
259     }
261     
262    
263     if (tiles[player.y * TILE_WIDTH] == BOX_CHAR)
264     {
265       gameState = GAME_STATE_GAMEOVER;
266       timeSinceGameOverMs = millis();
267     }
269    
270     lcd.clear();
272     lcd.setCursor(player.x, player.y);
273     lcd.write('X');
275     //int lolIndex = 0 * TILE_WIDTH + 10;
276     //tiles[lolIndex] = 'E';
277     //tiles[lolIndex + TILE_WIDTH] = 'A';
278     
279    
280     for (int y = 0; y < SCREEN_TILE_HEIGHT; y++)
281     {
282       lcd.setCursor(0, y);
283       for (int x = 0; x < SCREEN_TILE_WIDTH; x++)
284       {
285         if (tiles[y * TILE_WIDTH + x] != ' ')
286         {
287           lcd.setCursor(x, y);
288           lcd.write(tiles[y * TILE_WIDTH + x]);
289         }
290       }
291     }
292     
293     
294     //Idea: everytime player switches Y position, make it flash so that it 
295     // will be clear that player switched its position
299     break;
300   }
301   delay(10);
303   frame++;