From 311f8bbc1a6a2f38ca3974636e2fb449de3b5a47 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sat, 9 Jun 2012 13:49:01 +1000 Subject: [PATCH] Snake game that combines DMD and IRreceiver --- doc/ir-snake.dox | 31 +++++ doc/mainpage.dox | 4 + libraries/IR/examples/Snake/Snake.pde | 190 ++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 doc/ir-snake.dox create mode 100644 libraries/IR/examples/Snake/Snake.pde diff --git a/doc/ir-snake.dox b/doc/ir-snake.dox new file mode 100644 index 00000000..5cf9bc65 --- /dev/null +++ b/doc/ir-snake.dox @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** +\file ir-snake.dox +\page ir_snake Snake Video Game Using an Infrared Remote Control + +This example demonstrates the use of the DMD and IRreceiver classes. +The full source code follows: + +\include IR/examples/Snake/Snake.pde +*/ diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 4848ccd5..3792842b 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -49,6 +49,8 @@ LCD shield. \li \ref dmd_demo "Demo" that shows off various bitmap drawing features. \li \ref dmd_running_figure "RunningFigure" example that demonstrates how to draw and animate bitmaps. +\li \ref ir_snake "Snake" game that combines the dot matrix display with +IRreceiver to make a simple video game. \section main_BlinkLED BlinkLED Utility Library @@ -90,6 +92,8 @@ realtime clock and the LCD library to implement an alarm clock. \li IRreceiver class that receives incoming RC-5 commands from an infrared remote control. \li \ref ir_dumpir "DumpIR" example that dumps all incoming RC-5 commands. +\li \ref ir_snake "Snake" game that combines DMD with an infrared remote +control to make a simple video game. \section main_other Other diff --git a/libraries/IR/examples/Snake/Snake.pde b/libraries/IR/examples/Snake/Snake.pde new file mode 100644 index 00000000..2b015b9b --- /dev/null +++ b/libraries/IR/examples/Snake/Snake.pde @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2012 Southern Storm Software, Pty Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#define SNAKE_ADVANCE_TIME 150 +#define SNAKE_BLINK_TIME 500 +#define SNAKE_INC_STEPS 5 +#define SNAKE_MAX_LENGTH 50 +#define SNAKE_START_LENGTH 10 + +struct Point +{ + int x, y; +}; + +DMD display; +IRreceiver ir; +bool paused; +bool gameOver; +bool waitForStart; +bool snakeDrawn; +unsigned long lastChange; +Point direction; +Point snakeParts[SNAKE_MAX_LENGTH]; +int snakeLength; +int incStep; + +ISR(TIMER1_OVF_vect) +{ + display.refresh(); +} + +void setup() { + display.enableTimer1(); + display.setFont(Mono5x7); + startGame(); +} + +void drawSnake(Bitmap::Color color) { + for (int index = 0; index < snakeLength; ++index) + display.setPixel(snakeParts[index].x, snakeParts[index].y, color); +} + +void loop() { + // Handle the "Game Over" state. Any key press starts a new game. + int cmd = ir.command(); + if (gameOver) { + if (cmd != -1 && (cmd & IRreceiver::AUTO_REPEAT) == 0) + startGame(); + return; + } + + // Pause the game if waiting for the first start. While waiting, + // blink the location of the snake to help the player get their bearings. + if (waitForStart) { + if (cmd == -1) { + if ((millis() - lastChange) >= SNAKE_BLINK_TIME) { + snakeDrawn = !snakeDrawn; + drawSnake(snakeDrawn ? Bitmap::White : Bitmap::Black); + lastChange += SNAKE_BLINK_TIME; + } + return; + } + drawSnake(Bitmap::White); + waitForStart = false; + snakeDrawn = true; + lastChange = millis(); + } + + // Process commands from the player. + switch (cmd) { + + // Both arrow keys and numbers can be used to control the direction, + // in case the remote control does not have arrow keys. + case RC5_LEFT: case RC5_4: + changeDirection(-1, 0); + break; + case RC5_RIGHT: case RC5_6: + changeDirection(1, 0); + break; + case RC5_UP: case RC5_2: + changeDirection(0, -1); + break; + case RC5_DOWN: case RC5_8: + changeDirection(0, 1); + break; + + case RC5_PAUSE: case RC5_PLAY: case RC5_0: + // Pause or resume the game. + paused = !paused; + lastChange = millis(); + break; + + case RC5_STOP: case RC5_STANDBY: + // Stop the game and start a new one. + startGame(); + break; + } + + // Advance the snake position if not paused and the timeout has expired. + if (!paused && (millis() - lastChange) >= SNAKE_ADVANCE_TIME) { + ++incStep; + advanceSnake(incStep >= SNAKE_INC_STEPS); + lastChange += SNAKE_ADVANCE_TIME; + } +} + +void startGame() { + randomSeed(micros() + analogRead(A0)); // Analog read adds some noise. + display.clear(); + display.drawRect(0, 0, display.width() - 1, display.height() - 1); + for (int count = 0; count < 10; ++count) { + int x, y; + if (random(0, 2) == 0) { + x = random(1, display.width() - 5); + y = random(1, display.height() - 1); + display.drawLine(x, y, x + 4, y); + } else { + x = random(1, display.width() - 1); + y = random(1, display.height() - 3); + display.drawLine(x, y, x, y + 2); + } + } + paused = false; + gameOver = false; + waitForStart = true; + snakeDrawn = true; + lastChange = millis(); + direction.x = 1; + direction.y = 0; + incStep = 0; + snakeLength = SNAKE_START_LENGTH; + for (int index = 0; index < snakeLength; ++index) { + snakeParts[index].x = 3 + index; + snakeParts[index].y = 4; + display.setPixel + (snakeParts[index].x, snakeParts[index].y, Bitmap::White); + } +} + +void changeDirection(int x, int y) { + direction.x = x; + direction.y = y; +} + +void advanceSnake(bool increase) { + int x = snakeParts[snakeLength - 1].x + direction.x; + int y = snakeParts[snakeLength - 1].y + direction.y; + if (display.pixel(x, y) == Bitmap::White) { + gameOver = true; + display.clear(); + display.drawText(5, 0, "Game"); + display.drawText(3, 8, "Over!"); + return; + } + if (!increase || snakeLength >= SNAKE_MAX_LENGTH) { + display.setPixel(snakeParts[0].x, snakeParts[0].y, Bitmap::Black); + for (int index = 0; index < snakeLength - 1; ++index) + snakeParts[index] = snakeParts[index + 1]; + } else { + ++snakeLength; + } + snakeParts[snakeLength - 1].x = x; + snakeParts[snakeLength - 1].y = y; + display.setPixel(x, y, Bitmap::White); + if (increase) + incStep = 0; +}