- ابزارآلات و آهنربا
-
ابزارآلات
- ابزار اندازه گیری (مولتیمتر، کولیس و . . .)
- ابزار سوراخکاری (مته، سه نظام، چهار نظام، پنج نظام و . . .)
- ابزار و تجهیزات کار (پیچ گوشتی، انبردست، سیمچین، پنس و . . .)
- برد بورد، فیبر خام و سوراخدار (فیبر مدار چاپی و PCB)
- پیچ و اسپیسر (پیچ و مهره رباتیک و اسپیسر پلاستیکی و فلزی)
- سیم و کابل (سیم افشان، فلت، باندی، کابل شارژ، پرینتر، و . . .)
- لحیمکاری (هویه، نوک هویه، پایه هویه، سیم لحیم، روغن لحیم و . . . )
- منبع تغذیه، باتری و جاباتری (باتری قلمی، نیم قلمی، کتابی و . . . )
-
ابزارآلات
- برد هوشمند
- پرینتر سه بعدی
- رباتیک و پروازی
- ماژول
- قطعات الکترونیک
- کیت الکترونیکی و انواع جعبه
- موتور
- اصناف مختلف
اجرای بازی مار (Snake) نوکیا با آردوینو
حتماً شما نیز بازی نوستالژیک Snake در گوشیهای نوکیا را به خاطر میآورید. برخی اوقات دلتان برای این نوع بازیها تنگ میشود ولی امکان دسترسی به گوشیهای قدیمی برای سرگرم شدن با آن وجود ندارد. در این شرایط، میتوانید به آسانی اقدام به اجرای بازی مار (Snake) نوکیا با آردوینو کنید.
همانطور که به یاد میآورید، مار هر چه بیشتر غذا میخورد، بزرگ و بزرگتر میشد و بازی تا زمانی ادامه داشت که مار در یک نقطه خاصی با خودش برخورد میکرد. در واقع، هر چه مار بزرگتر میشد، کنترل کردن آن سختتر میگردید اما از طرفی امتیاز ارتقاء مییافت.
فهرست مطالب
در پروژه اجرای بازی مار (Snake) نوکیا با آردوینو چه چیزی میآموزیم؟
همانگونه که گفتیم، میخواهیم بازی Snake گوشیهای نوکیا را با یک برد آردوینو شبیهسازی کنیم. برای انجام این کار به یک ماژول جویاستیک و یک LCD نیاز خواهیم داشت. دو روش برای اجرای این پروژه وجود دارد؛ یکی بهرهمندی از ماژول دات ماتریکس Max7219 8×8 و دیگری LCD کاراکتری 1602.
لوازم مورد نیاز برای پروژه اجرای بازی مار (Snake) نوکیا با آردوینو
شما میتوانید با کلیک کردن روی دکمه خرید هر کدام از لوازم زیر، کالای مدنظر را تهیه کنید.
اجرای بازی مار (Snake) نوکیا با آردوینو با نمایشگر دات ماتریکس Max7219 8x8
مرحله اول پروژه اجرای بازی مار (Snake) نوکیا با آردوینو؛ سیمکشی مدار
ابتدا قطعات الکترونیکی را مطابق تصویر زیر به آردوینو وصل کنید.
نکته: با پتانسیومتر میتوانید سرعت بازی را کم و زیاد کنید. هر چه سرعت بازی بیشتر شود، سختتر خواهد شد.
مرحله دوم پروژه اجرای بازی مار (Snake) نوکیا با آردوینو؛ کدنویسی
پس از وصل کردن قطعات به برد آردوینو، کد زیر را در نرمافزار Arduino IDE آپلود نمایید.
/*
Made on 01 Sep 2021
Home
*/
#include "LedControl.h" // LedControl library is used for controlling a LED matrix. Find it using Library Manager or download zip here: https://github.com/wayoda/LedControl
// there are defined all the pins
struct Pin {
static const short joystickX = A5; // joystick X axis pin
static const short joystickY = A4; // joystick Y axis pin
static const short joystickVCC = 1; // virtual VCC for the joystick (Analog 1) (to make the joystick connectable right next to the arduino nano)
static const short joystickGND = 2; // virtual GND for the joystick (Analog 0) (to make the joystick connectable right next to the arduino nano)
static const short potentiometer = A3; // potentiometer for snake speed control
static const short CLK = 8; // clock for LED matrix
static const short CS = 9; // chip-select for LED matrix
static const short DIN = 10; // data-in for LED matrix
};
// LED matrix brightness: between 0(darkest) and 15(brightest)
const short intensity = 8;
// lower = faster message scrolling
const short messageSpeed = 5;
// initial snake length (1...63, recommended 3)
const short initialSnakeLength = 3;
void setup() {
Serial.begin(115200); // set the same baud rate on your Serial Monitor
initialize(); // initialize pins & LED matrix
calibrateJoystick(); // calibrate the joystick home (do not touch it)
showSnakeMessage(); // scrolls the 'snake' message around the matrix
}
void loop() {
generateFood(); // if there is no food, generate one
scanJoystick(); // watches joystick movements & blinks with food
calculateSnake(); // calculates snake parameters
handleGameStates();
// uncomment this if you want the current game board to be printed to the serial (slows down the game a bit)
// dumpGameBoard();
}
// --------------------------------------------------------------- //
// -------------------- supporting variables --------------------- //
// --------------------------------------------------------------- //
LedControl matrix(Pin::DIN, Pin::CLK, Pin::CS, 1);
struct Point {
int row = 0, col = 0;
Point(int row = 0, int col = 0): row(row), col(col) {}
};
struct Coordinate {
int x = 0, y = 0;
Coordinate(int x = 0, int y = 0): x(x), y(y) {}
};
bool win = false;
bool gameOver = false;
// primary snake head coordinates (snake head), it will be randomly generated
Point snake;
// food is not anywhere yet
Point food(-1, -1);
// construct with default values in case the user turns off the calibration
Coordinate joystickHome(500, 500);
// snake parameters
int snakeLength = initialSnakeLength; // chosen by the user in the config section
int snakeSpeed = 1; // will be set according to potentiometer value, cannot be 0
int snakeDirection = 0; // if it is 0, the snake does not move
// direction constants
const short up = 1;
const short right = 2;
const short down = 3; // 'down - 2' must be 'up'
const short left = 4; // 'left - 2' must be 'right'
// threshold where movement of the joystick will be accepted
const int joystickThreshold = 160;
// artificial logarithmity (steepness) of the potentiometer (-1 = linear, 1 = natural, bigger = steeper (recommended 0...1))
const float logarithmity = 0.4;
// snake body segments storage
int gameboard[8][8] = {};
// --------------------------------------------------------------- //
// -------------------------- functions -------------------------- //
// --------------------------------------------------------------- //
// if there is no food, generate one, also check for victory
void generateFood() {
if (food.row == -1 || food.col == -1) {
// self-explanatory
if (snakeLength >= 64) {
win = true;
return; // prevent the food generator from running, in this case it would run forever, because it will not be able to find a pixel without a snake
}
// generate food until it is in the right position
do {
food.col = random(8);
food.row = random(8);
} while (gameboard[food.row][food.col] > 0);
}
}
// watches joystick movements & blinks with food
void scanJoystick() {
int previousDirection = snakeDirection; // save the last direction
long timestamp = millis();
while (millis() < timestamp + snakeSpeed) {
// calculate snake speed exponentially (10...1000ms)
float raw = mapf(analogRead(Pin::potentiometer), 0, 1023, 0, 1);
snakeSpeed = mapf(pow(raw, 3.5), 0, 1, 10, 1000); // change the speed exponentially
if (snakeSpeed == 0) snakeSpeed = 1; // safety: speed can not be 0
// determine the direction of the snake
analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold ? snakeDirection = up : 0;
analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold ? snakeDirection = down : 0;
analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold ? snakeDirection = left : 0;
analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold ? snakeDirection = right : 0;
// ignore directional change by 180 degrees (no effect for non-moving snake)
snakeDirection + 2 == previousDirection && previousDirection != 0 ? snakeDirection = previousDirection : 0;
snakeDirection - 2 == previousDirection && previousDirection != 0 ? snakeDirection = previousDirection : 0;
// intelligently blink with the food
matrix.setLed(0, food.row, food.col, millis() % 100 < 50 ? 1 : 0);
}
}
// calculate snake movement data
void calculateSnake() {
switch (snakeDirection) {
case up:
snake.row--;
fixEdge();
matrix.setLed(0, snake.row, snake.col, 1);
break;
case right:
snake.col++;
fixEdge();
matrix.setLed(0, snake.row, snake.col, 1);
break;
case down:
snake.row++;
fixEdge();
matrix.setLed(0, snake.row, snake.col, 1);
break;
case left:
snake.col--;
fixEdge();
matrix.setLed(0, snake.row, snake.col, 1);
break;
default: // if the snake is not moving, exit
return;
}
// if there is a snake body segment, this will cause the end of the game (snake must be moving)
if (gameboard[snake.row][snake.col] > 1 && snakeDirection != 0) {
gameOver = true;
return;
}
// check if the food was eaten
if (snake.row == food.row && snake.col == food.col) {
food.row = -1; // reset food
food.col = -1;
// increment snake length
snakeLength++;
// increment all the snake body segments
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (gameboard[row][col] > 0 ) {
gameboard[row][col]++;
}
}
}
}
// add new segment at the snake head location
gameboard[snake.row][snake.col] = snakeLength + 1; // will be decremented in a moment
// decrement all the snake body segments, if segment is 0, turn off the corresponding led
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
// if there is a body segment, decrement it's value
if (gameboard[row][col] > 0 ) {
gameboard[row][col]--;
}
// display the current pixel
matrix.setLed(0, row, col, gameboard[row][col] == 0 ? 0 : 1);
}
}
}
// causes the snake to appear on the other side of the screen if it gets out of the edge
void fixEdge() {
snake.col < 0 ? snake.col += 8 : 0;
snake.col > 7 ? snake.col -= 8 : 0;
snake.row < 0 ? snake.row += 8 : 0;
snake.row > 7 ? snake.row -= 8 : 0;
}
void handleGameStates() {
if (gameOver || win) {
unrollSnake();
showScoreMessage(snakeLength - initialSnakeLength);
if (gameOver) showGameOverMessage();
else if (win) showWinMessage();
// re-init the game
win = false;
gameOver = false;
snake.row = random(8);
snake.col = random(8);
food.row = -1;
food.col = -1;
snakeLength = initialSnakeLength;
snakeDirection = 0;
memset(gameboard, 0, sizeof(gameboard[0][0]) * 8 * 8);
matrix.clearDisplay(0);
}
}
void unrollSnake() {
// switch off the food LED
matrix.setLed(0, food.row, food.col, 0);
delay(800);
// flash the screen 5 times
for (int i = 0; i < 5; i++) {
// invert the screen
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
matrix.setLed(0, row, col, gameboard[row][col] == 0 ? 1 : 0);
}
}
delay(20);
// invert it back
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
matrix.setLed(0, row, col, gameboard[row][col] == 0 ? 0 : 1);
}
}
delay(50);
}
delay(600);
for (int i = 1; i <= snakeLength; i++) {
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (gameboard[row][col] == i) {
matrix.setLed(0, row, col, 0);
delay(100);
}
}
}
}
}
// calibrate the joystick home for 10 times
void calibrateJoystick() {
Coordinate values;
for (int i = 0; i < 10; i++) {
values.x += analogRead(Pin::joystickX);
values.y += analogRead(Pin::joystickY);
}
joystickHome.x = values.x / 10;
joystickHome.y = values.y / 10;
}
void initialize() {
pinMode(Pin::joystickVCC, OUTPUT);
digitalWrite(Pin::joystickVCC, HIGH);
pinMode(Pin::joystickGND, OUTPUT);
digitalWrite(Pin::joystickGND, LOW);
matrix.shutdown(0, false);
matrix.setIntensity(0, intensity);
matrix.clearDisplay(0);
randomSeed(analogRead(A5));
snake.row = random(8);
snake.col = random(8);
}
void dumpGameBoard() {
String buff = "\n\n\n";
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if (gameboard[row][col] < 10) buff += " ";
if (gameboard[row][col] != 0) buff += gameboard[row][col];
else if (col == food.col && row == food.row) buff += "@";
else buff += "-";
buff += " ";
}
buff += "\n";
}
Serial.println(buff);
}
// --------------------------------------------------------------- //
// -------------------------- messages --------------------------- //
// --------------------------------------------------------------- //
const PROGMEM bool snakeMessage[8][84] = {
{0,0,0,0,0,0,0,0, 0,1,1,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,1,0,0,1,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,0,0,0,0,1,1,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0,0,0,0,0,0,0,0, 0,1,1,0,0,0,0,1,1,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1, 0,1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
const PROGMEM bool gameOverMessage[8][90] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
const PROGMEM bool scoreMessage[8][58] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
const PROGMEM bool digits[][8][8] = {
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 1, 1, 1, 0},
{0, 1, 1, 1, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 0},
{0, 1, 1, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{0, 0, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 1, 1, 1, 0, 0},
{0, 0, 1, 0, 1, 1, 0, 0},
{0, 1, 0, 0, 1, 1, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 1, 1, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0}
},
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 1, 1, 0},
{0, 0, 1, 1, 1, 1, 0, 0}
}
};
// scrolls the 'snake' message around the matrix
void showSnakeMessage() {
[&] {
for (int d = 0; d < sizeof(snakeMessage[0]) - 7; d++) {
for (int col = 0; col < 8; col++) {
delay(messageSpeed);
for (int row = 0; row < 8; row++) {
// this reads the byte from the PROGMEM and displays it on the screen
matrix.setLed(0, row, col, pgm_read_byte(&(snakeMessage[row][col + d])));
}
}
// if the joystick is moved, exit the message
if (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold
|| analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold
|| analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold
|| analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {
return; // return the lambda function
}
}
}();
matrix.clearDisplay(0);
// wait for joystick co come back
while (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold
|| analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold
|| analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold
|| analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {}
}
// scrolls the 'game over' message around the matrix
void showGameOverMessage() {
[&] {
for (int d = 0; d < sizeof(gameOverMessage[0]) - 7; d++) {
for (int col = 0; col < 8; col++) {
delay(messageSpeed);
for (int row = 0; row < 8; row++) {
// this reads the byte from the PROGMEM and displays it on the screen
matrix.setLed(0, row, col, pgm_read_byte(&(gameOverMessage[row][col + d])));
}
}
// if the joystick is moved, exit the message
if (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold
|| analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold
|| analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold
|| analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {
return; // return the lambda function
}
}
}();
matrix.clearDisplay(0);
// wait for joystick co come back
while (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold
|| analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold
|| analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold
|| analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {}
}
// scrolls the 'win' message around the matrix
void showWinMessage() {
// not implemented yet // TODO: implement it
}
// scrolls the 'score' message with numbers around the matrix
void showScoreMessage(int score) {
if (score < 0 || score > 99) return;
// specify score digits
int second = score % 10;
int first = (score / 10) % 10;
[&] {
for (int d = 0; d < sizeof(scoreMessage[0]) + 2 * sizeof(digits[0][0]); d++) {
for (int col = 0; col < 8; col++) {
delay(messageSpeed);
for (int row = 0; row < 8; row++) {
if (d <= sizeof(scoreMessage[0]) - 8) {
matrix.setLed(0, row, col, pgm_read_byte(&(scoreMessage[row][col + d])));
}
int c = col + d - sizeof(scoreMessage[0]) + 6; // move 6 px in front of the previous message
// if the score is < 10, shift out the first digit (zero)
if (score < 10) c += 8;
if (c >= 0 && c < 8) {
if (first > 0) matrix.setLed(0, row, col, pgm_read_byte(&(digits[first][row][c]))); // show only if score is >= 10 (see above)
} else {
c -= 8;
if (c >= 0 && c < 8) {
matrix.setLed(0, row, col, pgm_read_byte(&(digits[second][row][c]))); // show always
}
}
}
}
// if the joystick is moved, exit the message
if (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold
|| analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold
|| analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold
|| analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {
return; // return the lambda function
}
}
}();
matrix.clearDisplay(0);
// // wait for joystick co come back
// while (analogRead(Pin::joystickY) < joystickHome.y - joystickThreshold
// || analogRead(Pin::joystickY) > joystickHome.y + joystickThreshold
// || analogRead(Pin::joystickX) < joystickHome.x - joystickThreshold
// || analogRead(Pin::joystickX) > joystickHome.x + joystickThreshold) {}
}
// standard map function, but with floats
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
با استفاده از کد داده شده، میتوانید بازی Snake را با یک ماژول جویاستیک انجام دهید. برای جابهجا کردن مار موجود در این بازی، فقط کافی است جویاستیک را تکان دهید. غذای مار به شکل نقطه روی نمایشگر ظاهر میشود و هر بار که مار آن را میخورد، بزرگتر خواهد شد.
چند نکته در مورد پروژه اجرای بازی مار (Snake) نوکیا با آردوینو
- پینهای 8، 9 و 10 به عنوان خروجی آردوینو به ماژول نمایشگر دات ماتریکس Max7219 8×8 وصل میشوند.
- پتانسیومتر به منظور کنترل سرعت بازی، به پین آنالوگ A3 متصل میشود.
- محورهای X و Y به ترتیب به پینهای آنالوگ A5 و A4 وصل میشوند.
مرحله سوم پروژه اجرای بازی مار (Snake) نوکیا با آردوینو؛ بازی کردن
اینک پس از آپلود کردن کدها، میتوانید بازی را اجرا کنید. در ابتدا میبایست جویاستیک را جابهجا نمایید. سپس بازی تا زمانی ادامه پیدا خواهد کرد که سر مار به بدنه خودش برخورد نماید.
نکته: امتیاز دریافت شده در انتهای بازی نمایش داده خواهد شد.
ویدیوی زیر نحوه اجرای بازی را نشان میدهد.
اجرای بازی مار (Snake) نوکیا با آردوینو با نمایشگر LCD کاراکتری 1602
مرحله اول پروژه اجرای بازی مار (Snake) نوکیا با آردوینو؛ سیمکشی مدار
ابتدا قطعات الکترونیکی را مطابق تصویر زیر به برد آردوینو وصل کنید.
نکته 1: شما میتوانید شیلد LCD را روی آردوینو Uno قرار دهید.
نکته 2: دکمههای وسط برای کنترل حرکت مار استفاده میشوند. با این روش مار در چهار جهت بالا، پایین، راست و چپ حرکت خواهد کرد.
نکته 3: دکمه RST برای ریست کردن بازی استفاده میشود.
نکته 4: انتخاب گزینهها و آغاز بازی نیز با دکمه Select انجام میشود.
مرحله دوم پروژه اجرای بازی مار (Snake) نوکیا با آردوینو؛ کدنویسی
در این مرحله پس از اتصال برد آردوینو به رایانه، کد زیر را آپلود کنید.
/*
Made on 01 Sep 2021
Home
*/
#include
byte mySnake[8][8] =
{
{ B00000,
B00000,
B00011,
B00110,
B01100,
B11000,
B00000,
},
{ B00000,
B11000,
B11110,
B00011,
B00001,
B00000,
B00000,
},
{ B00000,
B00000,
B00000,
B00000,
B00000,
B11111,
B01110,
},
{ B00000,
B00000,
B00011,
B01111,
B11000,
B00000,
B00000,
},
{ B00000,
B11100,
B11111,
B00001,
B00000,
B00000,
B00000,
},
{ B00000,
B00000,
B00000,
B11000,
B01101,
B00111,
B00000,
},
{ B00000,
B00000,
B01110,
B11011,
B11111,
B01110,
B00000,
},
{ B00000,
B00000,
B00000,
B01000,
B10000,
B01000,
B00000,
}
};
boolean levelz[5][2][16] = {
{{false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false},
{false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false}},
{{true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true},
{true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true}},
{{true,false,false,false,true,false,false,false,false,false,false,true,false,false,false,true},
{true,false,false,false,false,false,false,false,true,false,false,false,false,false,false,true}},
{{true,false,true,false,false,false,false,false,false,true,false,false,false,true,false,false},
{false,false,false,false,true,false,false,true,false,false,false,true,false,false,false,true}}
};
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
unsigned long time, timeNow;
int gameSpeed;
boolean skip, gameOver, gameStarted;
int olddir;
int selectedLevel,levels;
int adc_key_val[5] ={50, 200, 400, 600, 800 };
int NUM_KEYS = 5;
int adc_key_in;
int key=-1;
int oldkey=-1;
boolean x[16][80];
byte myChar[8];
byte nullChar[8] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
boolean special;
struct partdef
{
int row,column,dir; //0 - up, 1 - down, 2 - right, 3 - left
struct partdef *next;
};
typedef partdef part;
part *head, *tail;
int i,j,collected;
long pc,pr;
void drawMatrix()
{
int cc=0;
if (!gameOver)
{
x[pr][pc] = true;
//for (i=0;i<8;i++) lcd.createChar(i, nullChar);
for(int r=0;r<2;r++)
{
for(int c=0;c<16;c++)
{
special = false;
for(int i=0;i<8;i++)
{
byte b=B00000;
if (x[r*8+i][c*5+0]) {b+=B10000; special = true;}
if (x[r*8+i][c*5+1]) {b+=B01000; special = true;}
if (x[r*8+i][c*5+2]) {b+=B00100; special = true;}
if (x[r*8+i][c*5+3]) {b+=B00010; special = true;}
if (x[r*8+i][c*5+4]) {b+=B00001; special = true;}
myChar[i] = b;
}
if (special)
{
lcd.createChar(cc, myChar);
lcd.setCursor(c,r);
lcd.write(byte(cc));
cc++;
}
else
{
lcd.setCursor(c,r);
if (levelz[selectedLevel][r][c]) lcd.write(255);
else lcd.write(254);
}
}
}
}
}
void freeList()
{
part *p,*q;
p = tail;
while (p!=NULL)
{
q = p;
p = p->next;
free(q);
}
head = tail = NULL;
}
void gameOverFunction()
{
delay(1000);
lcd.clear();
freeList();
lcd.setCursor(3,0);
lcd.print("Game Over!");
lcd.setCursor(4,1);
lcd.print("Score: ");
lcd.print(collected);
delay(1000);
}
void growSnake()
{
part *p;
p = (part*)malloc(sizeof(part));
p->row = tail->row;
p->column = tail->column;
p->dir = tail->dir;
p->next = tail;
tail = p;
}
void newPoint()
{
part *p;
p = tail;
boolean newp = true;
while (newp)
{
pr = random(16);
pc = random(80);
newp = false;
if (levelz[selectedLevel][pr / 8][pc / 5]) newp=true;
while (p->next != NULL && !newp)
{
if (p->row == pr && p->column == pc) newp = true;
p = p->next;
}
}
if (collected < 13 && gameStarted) growSnake();
}
void moveHead()
{
switch(head->dir) // 1 step in direction
{
case 0: head->row--; break;
case 1: head->row++; break;
case 2: head->column++; break;
case 3: head->column--; break;
default : break;
}
if (head->column >= 80) head->column = 0;
if (head->column < 0) head->column = 79;
if (head->row >= 16) head->row = 0;
if (head->row < 0) head->row = 15;
if (levelz[selectedLevel][head->row / 8][head->column / 5]) gameOver = true; // wall collision check
part *p;
p = tail;
while (p != head && !gameOver) // self collision
{
if (p->row == head->row && p->column == head->column) gameOver = true;
p = p->next;
}
if (gameOver)
gameOverFunction();
else
{
x[head->row][head->column] = true;
if (head->row == pr && head->column == pc) // point pickup check
{
collected++;
if (gameSpeed < 25) gameSpeed+=3;
newPoint();
}
}
}
void moveAll()
{
part *p;
p = tail;
x[p->row][p->column] = false;
while (p->next != NULL)
{
p->row = p->next->row;
p->column = p->next->column;
p->dir = p->next->dir;
p = p->next;
}
moveHead();
}
void createSnake(int n) // n = size of snake
{
for (i=0;i<16;i++)
for (j=0;j<80;j++)
x[i][j] = false;
part *p, *q;
tail = (part*)malloc(sizeof(part));
tail->row = 7;
tail->column = 39 + n/2;
tail->dir = 3;
q = tail;
x[tail->row][tail->column] = true;
for (i = 0; i < n-1; i++) // build snake from tail to head
{
p = (part*)malloc(sizeof(part));
p->row = q->row;
p->column = q->column - 1; //initial snake id placed horizoltally
x[p->row][p->column] = true;
p->dir = q->dir;
q->next = p;
q = p;
}
if (n>1)
{
p->next = NULL;
head = p;
}
else
{
tail->next = NULL;
head = tail;
}
}
void startF()
{
gameOver = false;
gameStarted = false;
selectedLevel = 1;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Select level: 1");
for(i=0;i<8;i++)
{
lcd.createChar(i,mySnake[i]);
lcd.setCursor(i+4,1);
lcd.write(byte(i));
}
collected = 0;
gameSpeed = 8;
createSnake(3);
time = 0;
}
void setup()
{
levels = 5; //number of lvls
lcd.begin(16, 2);
startF();
}
void loop()
{
if (!gameOver && !gameStarted)
{
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press
if (key != oldkey) // if keypress is detected
{
delay(50); // wait for debounce time
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press
if (key != oldkey)
{
oldkey = key;
if (key >=0)
{
olddir = head->dir;
if (key==1 && selectedLevel1) selectedLevel--;
if (key==4)
{
lcd.clear();
selectedLevel--;
newPoint();
gameStarted = true;
}
else
{
lcd.setCursor(14,0);
lcd.print(selectedLevel);
}
}
}
}
}
if (!gameOver && gameStarted)
{
skip = false; //skip the second moveAll() function call if the first was made
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press
if (key != oldkey) // if keypress is detected
{
delay(50); // wait for debounce time
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press
if (key != oldkey)
{
oldkey = key;
if (key >=0)
{
olddir = head->dir;
if (key==0 && head->dir!=3) head->dir = 2;
if (key==1 && head->dir!=1) head->dir = 0;
if (key==2 && head->dir!=0) head->dir = 1;
if (key==3 && head->dir!=2) head->dir = 3;
if (olddir != head->dir)
{
skip = true;
delay(1000/gameSpeed);
moveAll();
drawMatrix();
}
}
}
}
if (!skip)
{
timeNow = millis();
if (timeNow - time > 1000 / gameSpeed)
{
moveAll();
drawMatrix();
time = millis();
}
}
}
if(gameOver)
{
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press
if (key != oldkey) // if keypress is detected
{
delay(50); // wait for debounce time
adc_key_in = analogRead(0); // read the value from the sensor
key = get_key(adc_key_in); // convert into key press
if (key != oldkey)
{
oldkey = key;
if (key >=0)
{
startF();
}
}
}
}
}
int get_key(unsigned int input)
{
int k;
for (k = 0; k < NUM_KEYS; k++)
{
if (input < adc_key_val[k])
{
return k;
}
}
if (k >= NUM_KEYS)k = -1; // No valid key pressed
return k;
}
با استفاده از کد داده شده، میتوانید بازی Snake را به وسیله دکمههای روی شیلد LCD انجام دهید.
مرحله سوم پروژه اجرای بازی مار (Snake) نوکیا با آردوینو؛ بازی کردن
اینک پس از آپلود کردن برنامه، میتوانید شروع به بازی کردن کنید. بازی Snake با فشردن دکمه Select و انتخاب سطح آن آغاز خواهد شد و تا زمانی ادامه خواهد یافت که سر مار به بدن آن برخورد کند.
نکته 1: شما میتوانید سطح بازی را از 1 تا 5 مشخص کنید.
نکته 2: امتیازات شما در انتهای بازی روی نمایشگر پخش خواهند شد.
ویدیوی زیر نحوه اجرای بازی را نشان میدهد.
پس از اینکه پروژه گفته شده را انجام دادید، امکان به انجام رساندن پروژههای زیر را نیز خواهید داشت:
- استفاده از دیگر نمایشگرهای OLED و LCD
- اجرای دیگر بازیها با آردوینو و ماژول نمایشگر دات ماتریکس Max7219 8×8
- استفاده از ماتریکس مربعی 64 بیتی RGB WS2812