/*
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* LCD VSS pin to ground
* LCD VCC pin to 5V
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
*/
#define PADDLE_LEFT 3
#define PADDLE_RIGHT 4
#define length(arr) (sizeof(arr) / sizeof(arr[0]))
#define BUZZER 10
// include the library code:
#include <LiquidCrystal.h>
struct object
{
byte x;
byte y;
};
struct Inputs
{
byte x;
byte y;
byte button;
};
struct timer
{
int interval;
int new_t;
int old_t;
void (*callback)(void);
char times_left;
};
struct timer timers[9]; // I probably won't ever need more;
void setTimeOut(int interval, void (*callback)(void))
{
for(int i = 0; i < length(timers); i++)
{
if(timers[i].times_left == 0)
{
timers[i].times_left = 1; //if the timer is free than set the amount of times it runs at to one;
timers[i].interval = interval;
timers[i].callback = callback;
Serial.print(i);
Serial.print("\n");
break;
}
}
}
void repeatTime(int interval, void (*callback)(void))
{
for(int i = 0; i < length(timers); i++){
if(timers[i].times_left == 0)
{
timers[i].times_left = -1; //if the timer is free than set the amount of times it runs at to one;
timers[i].interval = interval;
timers[i].callback = callback;
break;
}
}
}
const byte paddle_left_high[] =
{
0b00011,
0b00011,
0b00011,
0b00011,
0b00000,
0b00000,
0b00000,
0b00000
};
const byte paddle_left_low[] =
{
0b00000,
0b00000,
0b00000,
0b00000,
0b00011,
0b00011,
0b00011,
0b00011
};
const byte paddle_left_mid[] =
{
0b00000,
0b00000,
0b00011,
0b00011,
0b00011,
0b00011,
0b00000,
0b00000
};
const byte paddle_right_high[] =
{
0b11000,
0b11000,
0b11000,
0b11000,
0b00000,
0b00000,
0b00000,
0b00000
};
const byte paddle_right_mid[] =
{
0b00000,
0b00000,
0b11000,
0b11000,
0b11000,
0b11000,
0b00000,
0b00000
};
const byte paddle_right_low[] =
{
0b00000,
0b00000,
0b00000,
0b00000,
0b11000,
0b11000,
0b11000,
0b11000
};
const byte ball_high[] =
{
0b11100,
0b11100,
0b11100,
0b00000,
0b00000,
0b00000,
0000000,
0b00000
};
const byte ball_mid[] =
{
0b00000,
0b00000,
0b11100,
0b11100,
0b11100,
0b00000,
0b00000,
0b00000
};
const byte ball_low[] =
{
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b11100,
0b11100,
0b11100
};
const byte* paddle_left_sprites[3];
const byte* paddle_right_sprites[3];
struct object left_paddle = {0,0};
struct object right_paddle = {15,0};
struct object ball = {1,0};
struct Inputs inputs;
int time1Old, time1New, time2Old, time2New;
int physicsInterval = 500;
bool gameOver = false;
byte ball_direction_x = 1;
byte ball_direction_y = 1;
//for the joystick
byte VRx = A1;
byte VRy = A0;
int xPosition = 0;
int yPosition = 0;
byte mapX = 0;
int mapY = 0;
byte player_score = 0, enemy_score = 0;
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void get_inputs(struct Inputs* inputs)
{
xPosition = analogRead(VRx);
yPosition = analogRead(VRy);
mapX = map(xPosition, 0, 1023, 16, 0);
mapY = map(yPosition, 0, 1023, 16, 0);
inputs->x = mapX;
inputs->y = mapY;
}
void playStop()
{
noTone(BUZZER);
}
void playTone(int frequency, int play_length)
{
tone(BUZZER, frequency);
setTimeOut(100, playStop);
}
inline void physicsLoop()
{
ball.x += ball_direction_x;
ball.y += ball_direction_y;
if(ball.x >= 14)
{
ball_direction_x = -1;
physicsInterval -= 20;
if(ball.y >= right_paddle.y -1 && ball.y <= right_paddle.y + 1)
{
//playTone(3000, 50);
ball_direction_x = -1;
ball_direction_y = random(-1,1);
}
else
{
player_score++;
delay(200);
gameOver = true;
}
}
else if(ball.x <= 1) {
if(ball.y >= left_paddle.y -1 && ball.y <= left_paddle.y + 1)
{
physicsInterval -= 20;
ball_direction_x = 1;
//playTone(3000, 50);
if(ball.y > left_paddle.y) ball_direction_y = 1;
else if(ball.y < left_paddle.y) ball_direction_y = -1;
else ball_direction_y = 0;
}
else
{
enemy_score++;
delay(200);
gameOver = true;
}
}
if(ball.y == 0) ball_direction_y = 1;
else if(ball.y == 5) ball_direction_y = -1;
time1Old = time1New;
}
inline void render()
{
lcd.setCursor(0, (left_paddle.y < 3 ? 0 : 1));
lcd.write(byte(PADDLE_LEFT));
lcd.setCursor(ball.x, (ball.y < 3 ? 0 : 1));
lcd.write(byte(ball.y < 3 ? ball.y : ball.y - 3));
lcd.setCursor(15, (right_paddle.y < 3 ? 0 : 1));
lcd.write(byte(PADDLE_RIGHT));
}
inline void inputLoop()
{
get_inputs(&inputs);
if(inputs.y <= 6 && left_paddle.y < 5)
{
left_paddle.y++;
uint8_t* sprite = (uint8_t*) paddle_left_sprites[left_paddle.y < 3 ? left_paddle.y : left_paddle.y - 3];
lcd.createChar(byte(PADDLE_LEFT), sprite);
}
else if(inputs.y >= 12 && left_paddle.y > 0)
{
left_paddle.y--;
uint8_t* sprite = (uint8_t*) paddle_left_sprites[left_paddle.y < 3 ? left_paddle.y : left_paddle.y - 3];
lcd.createChar(byte(PADDLE_LEFT), sprite);
}
}
inline void reset()
{ right_paddle.y = 0;
left_paddle.y = 0;
ball.x = 1;
ball.y = 0;
ball_direction_x = 1;
ball_direction_y = 1;
gameOver = false;
physicsInterval = 500;
}
inline void botLoop()
{
time2Old = time2New;
time2New = millis();
if(ball.x + 1> 5)
{
if(ball.y > right_paddle.y && right_paddle.y < 5) {
right_paddle.y++;
}
else if(ball.y < right_paddle.y && right_paddle.y > 1) {
right_paddle.y--;
}
const byte* sprite = paddle_right_sprites[(right_paddle.y < 3) ? right_paddle.y : (right_paddle.y - 3)];
lcd.createChar(byte(PADDLE_RIGHT), sprite);
}
}
void setup() {
//for the serial port
Serial.begin(9600);
//set up pins for the joystick
pinMode(VRx, INPUT);
pinMode(VRy, INPUT);
paddle_left_sprites[0] = paddle_left_high;
paddle_left_sprites[1] = paddle_left_mid;
paddle_left_sprites[2] = paddle_left_low;
paddle_right_sprites[0] = paddle_right_high;
paddle_right_sprites[1] = paddle_right_mid;
paddle_right_sprites[2] = paddle_right_low;
lcd.createChar(byte(0), (uint8_t*) ball_high);
lcd.createChar(byte(1), (uint8_t*) ball_mid);
lcd.createChar(byte(2), (uint8_t*) ball_low);
lcd.createChar(byte(PADDLE_LEFT), (uint8_t*) paddle_left_high);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
repeatTime(physicsInterval, physicsLoop);
repeatTime(400, botLoop);
time1Old = time2Old = millis();
time1New = time2New = time1Old;
randomSeed(analogRead(0));
reset();
}
void loop() {
lcd.clear();
if(!gameOver){
inputLoop();
render();
for(int i = 0; i < length(timers); i++)
{
if(timers[i].times_left != 0)
{
if(timers[i].new_t - timers[i].old_t > timers[i].interval)
{
timers[i].callback();
timers[i].old_t = timers[i].new_t;
if(timers[i].times_left > 0) timers[i].times_left--;
}
timers[i].new_t = millis();
}
else
{
timers[i].times_left = 0;
//timers[i].callback == NULL;
timers[i].interval = 0;
timers[i].old_t = 0;
timers[i].new_t = 0;
}
}
/*if((time1New - time1Old) > physicsInterval)
{
physicsLoop();
}
if((time2New-time2Old) > 400)
{
botLoop();
}*/
}
else
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Player score: ");
lcd.print(player_score);
lcd.setCursor(0,1);
lcd.print("Enemy score: ");
lcd.print(enemy_score);
if(enemy_score > 9){
delay(1000);
lcd.setCursor(1,0);
lcd.print("The enemy wins!");
enemy_score = 0;
player_score = 0;
}
else if(player_score > 9){
delay(1000);
lcd.setCursor(1,0);
lcd.print("You win!");
enemy_score = 0;
player_score = 0;
}
if(time1New - time1Old > 1000){
reset();
}
}
time1New = time2New = millis();
delay(60);
}