1 // Runner game by Johannes (dolphinana)
2 // Waived under CC0 1.0 (public domain)
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
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;
42 #define SCREEN_TILE_WIDTH 16
43 #define SCREEN_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;
54 tiles[tileY * TILE_WIDTH + tileX] = ' ';
59 /* If the given position doesn't have the tile, then the function
60 will return 0, which means not successful. */
66 if (digitalRead(BUTTON_PIN))
69 _buttonState = BUTTON_JUST_PRESSED;
71 _buttonState = BUTTON_PRESSED;
74 _buttonState = BUTTON_RELEASED;
77 unsigned char isButtonPressed()
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;
92 if (tiles[newIndex] != ' ')
100 tiles[newIndex] = BOX_CHAR;
108 for (int i = 0; i < TILE_WIDTH*TILE_HEIGHT; i++)
114 for (int i = 0; i < BOX_MAX; i++)
119 gameState = GAME_STATE_PLAYING;
124 // Adding serial debuggin stuff makes the program 800 bytes larger
127 Serial.println("Gayme :)");
131 pinMode(BUTTON_PIN, INPUT);
134 gameState = GAME_STATE_MENU;
138 timeSinceGameOverMs = 0;
139 reservedObstacles = 0;
151 digitalWrite(13, LOW);
155 case GAME_STATE_GAMEOVER:
157 lcd.print("Game Over");
159 if (millis() > timeSinceGameOverMs + GAME_OVER_COOLDOWN_MS)
162 lcd.print("Try Again?");
164 if (isButtonPressed())
168 case GAME_STATE_MENU:
170 lcd.print("Runner Game OwO");
173 if (isButtonPressed())
179 case GAME_STATE_PLAYING:
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.
188 for (int y = 0; y < TILE_HEIGHT; y++)
190 for (int x = 0; x < TILE_WIDTH; x++)
192 if (tiles[y * TILE_WIDTH + x] == BOX_CHAR)
194 // Move the obstacles (box) to the left
195 tiles[y * TILE_WIDTH + x] = ' ';
198 tiles[y * TILE_WIDTH + x - 1] = BOX_CHAR;
208 The scope of this if statement below will spawn reserved obstacles,
211 if (reservedObstacles > 0)
214 if (!spawnObstacle())
221 if (isButtonPressed() == BUTTON_JUST_PRESSED)
223 player.y = !player.y;
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.
232 const int x = SCREEN_TILE_WIDTH - 1;
234 /* if the obstacles look like this #
237 if (tiles[x] == BOX_CHAR && tiles[x + TILE_WIDTH] == BOX_CHAR)
239 removeObstacle(x, random(TILE_HEIGHT));
242 /* if the obstacles look like this #
245 if (tiles[0 * TILE_WIDTH + x] == BOX_CHAR &&
246 tiles[1 * TILE_WIDTH + x - 1] == BOX_CHAR)
248 removeObstacle(x, 0);
251 /* if the obstacles look like this #
254 if (tiles[0 * TILE_WIDTH + x - 1] == BOX_CHAR &&
255 tiles[1 * TILE_WIDTH + x] == BOX_CHAR)
257 removeObstacle(x, 1);
263 if (tiles[player.y * TILE_WIDTH] == BOX_CHAR)
265 gameState = GAME_STATE_GAMEOVER;
266 timeSinceGameOverMs = millis();
272 lcd.setCursor(player.x, player.y);
275 //int lolIndex = 0 * TILE_WIDTH + 10;
276 //tiles[lolIndex] = 'E';
277 //tiles[lolIndex + TILE_WIDTH] = 'A';
280 for (int y = 0; y < SCREEN_TILE_HEIGHT; y++)
283 for (int x = 0; x < SCREEN_TILE_WIDTH; x++)
285 if (tiles[y * TILE_WIDTH + x] != ' ')
288 lcd.write(tiles[y * TILE_WIDTH + x]);
294 //Idea: everytime player switches Y position, make it flash so that it
295 // will be clear that player switched its position