Skip to content

Instantly share code, notes, and snippets.

@parzibyte
Created March 10, 2018 00:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parzibyte/5343de41e0d63b03035f3b0d3dd038dc to your computer and use it in GitHub Desktop.
Save parzibyte/5343de41e0d63b03035f3b0d3dd038dc to your computer and use it in GitHub Desktop.
/*
Simular juego de batalla naval en Arduino
@date 9 de diciembre del 2017
@author parzibyte
@web https://www.parzibyte.me/blog
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>
#define DIRECCION_LCD 0x3F
#define ALTURA_LCD 4
#define ANCHURA_LCD 20
#define PIN_CLOCK 2
#define PIN_LATCH 3
#define PIN_DATA 4
/*
Número de espacios que
ocupa cada barco
*/
#define LONGITUD_FRAGATAS 1
#define LONGITUD_DESTRUCTORES 2
#define LONGITUD_ACORAZADOS 3
#define LONGITUD_SUBMARINOS 3
#define LONGITUD_PORTA_AVIONES 4
/*
Símbolos
*/
#define DISPARO_NO_ACERTADO '*'
#define DISPARO_ACERTADO '_'
#define AGUA ' '
#define FRAGATA_1 'A'
#define FRAGATA_2 'B'
#define DESTRUCTOR_1 'C'
#define DESTRUCTOR_2 'D'
#define ACORAZADO_1 'E'
#define SUBMARINO_1 'F'
#define PORTA_AVIONES_1 'G'
#define BARCO_IMPRIMIBLE '#'
#define DEBERIA_IMPRIMIR_LETRAS false
#define INTENTOS_MAXIMOS 18
/*
Pines
*/
#define BUZZER 7
/*
Duraciones
*/
#define DURACION_BUZZER_DISPARO_ACERTADO 200
#define DURACION_BUZZER_DISPARO_NO_ACERTADO 300
//Para interactuar con la LCD
LiquidCrystal_I2C lcd(DIRECCION_LCD, ANCHURA_LCD, ALTURA_LCD);
/*
Los puertos seriales de
cada jugador
*/
SoftwareSerial serialJugadorUno(10, 11);//TX, RX
SoftwareSerial serialJugadorDos(8, 9);
/*
El arreglo que representa el escenario
que será pintado en la pantalla
*/
char escenario[ANCHURA_LCD][ALTURA_LCD];
/*
Algunas otras variables útiles
*/
int intentosJugadorDos = 0;
byte leds = B00000000; //Lo que será mandado al registro de corrimiento
/*
Setup y loop
*/
void setup() {
prepararLcd();
establecerPines();
serialJugadorUno.begin(9600);
serialJugadorDos.begin(9600);
vaciarEscenario();
dibujarEscenario();
refrescarLeds();
/*
Ciclo infinito. Esperar
hasta que todos los barcos
estén asignados
*/
pedirUbicacionDeBarcos();
/*
Ahora es el turno del jugador 2
*/
turnoDelJugadorDos();
/*Fin*/
}
void loop() {
/*Nada por aquí*/
}
/*
Se encarga de poner
agua en todo el escenario
*/
void vaciarEscenario() {
for (int x = 0; x < ANCHURA_LCD; x++)
for (int y = 0; y < ALTURA_LCD; y++)
escenario[x][y] = AGUA;
}
void refrescarLeds() {
digitalWrite(PIN_LATCH, LOW);
shiftOut(PIN_DATA, PIN_CLOCK, MSBFIRST, leds);
digitalWrite(PIN_LATCH, HIGH);
}
/*
Volca o dibuja
el arreglo en
la LCD
*/
void dibujarEscenario() {
lcd.clear();
for (int x = 0; x < ANCHURA_LCD; x++) {
for (int y = 0; y < ALTURA_LCD; y++) {
lcd.setCursor(x, y);
if (DEBERIA_IMPRIMIR_LETRAS) {
switch (escenario[x][y]) {
case FRAGATA_1:
lcd.print(FRAGATA_1);
break;
case FRAGATA_2:
lcd.print(FRAGATA_2);
break;
case DESTRUCTOR_1:
lcd.print(DESTRUCTOR_1);
break;
case DESTRUCTOR_2:
lcd.print(DESTRUCTOR_2);
break;
case ACORAZADO_1:
lcd.print(ACORAZADO_1);
break;
case SUBMARINO_1:
lcd.print(SUBMARINO_1);
break;
case PORTA_AVIONES_1:
lcd.print(PORTA_AVIONES_1);
break;
case AGUA:
lcd.print(AGUA);
break;
case DISPARO_NO_ACERTADO:
case DISPARO_ACERTADO:
lcd.print(DISPARO_NO_ACERTADO);
break;
}
} else {
switch (escenario[x][y]) {
case FRAGATA_1:
case FRAGATA_2:
case DESTRUCTOR_1:
case DESTRUCTOR_2:
case ACORAZADO_1:
case SUBMARINO_1:
case PORTA_AVIONES_1:
lcd.print(BARCO_IMPRIMIBLE);
break;
case AGUA:
lcd.print(AGUA);
break;
case DISPARO_NO_ACERTADO:
case DISPARO_ACERTADO:
lcd.print(DISPARO_NO_ACERTADO);
break;
}
}
}
}
}
void prepararLcd() {
lcd.init();
lcd.backlight();
lcd.clear();
}
void turnoDelJugadorDos() {
String coordenadas;
/*
Escuchar y dar bienvenida
*/
serialJugadorDos.listen();
serialJugadorDos.println("Bienvenido, jugador 2");
int barcosDestruidos = 0;
boolean intentosAgotados = false, todosLosBarcosDestruidos = false;
do {
serialJugadorDos.println("Te quedan " + String(INTENTOS_MAXIMOS - intentosJugadorDos) + " intentos. Ingresa las coordenadas para realizar el ataque: ");
while (!serialJugadorDos.available());
coordenadas = serialJugadorDos.readString();
int x, y;
boolean deberiaRestarIntento = true;
if (esCoordenadaValida(coordenadas, x, y)) {
char objetivo = dispararYObtenerObjetivo(x, y);
switch (objetivo) {
case AGUA:
serialJugadorDos.println("Disparo no acertado. Le diste al agua.");
marcarDisparoNoAcertadoEn(x, y);
indicarDisparoErroneo();
break;
case FRAGATA_1:
case FRAGATA_2:
case DESTRUCTOR_1:
case DESTRUCTOR_2:
case ACORAZADO_1:
case SUBMARINO_1:
case PORTA_AVIONES_1:
serialJugadorDos.println("Disparo acertado!");
marcarDisparoAcertadoEn(x, y);
indicarDisparoAcertado();
break;
case DISPARO_ACERTADO:
case DISPARO_NO_ACERTADO:
deberiaRestarIntento = false;
serialJugadorDos.println("Ya habias disparado a esas coordenadas. Intenta en otro lugar");
break;
}
if (!hayMasInstanciasDe(objetivo)) {
apagarLedDe(objetivo);
barcosDestruidos++;
//Right here, restar los barcos
}
} else {
serialJugadorDos.println("Coordenadas invalidas o fuera de rango");
deberiaRestarIntento = false;
}
if (deberiaRestarIntento) intentosJugadorDos++;
dibujarEscenario();
intentosAgotados = intentosJugadorDos >= INTENTOS_MAXIMOS;
todosLosBarcosDestruidos = barcosDestruidos >= 7;
if (intentosAgotados || todosLosBarcosDestruidos) break;
} while (true);
if (todosLosBarcosDestruidos) {
jugadorDosGana();
} else if (intentosAgotados) {
jugadorDosPierde();
}
}
void jugadorDosPierde() {
serialJugadorDos.println("Pierdes");
serialJugadorUno.println("Ganas");
encenderBuzzer();
delay(2500);
apagarBuzzer();
}
void jugadorDosGana() {
serialJugadorDos.println("Ganas");
serialJugadorUno.println("Pierdes");
for (int x = 0; x < 10; x++) {
encenderBuzzer();
delay(125);
apagarBuzzer();
delay(125);
}
}
void encenderLedDe(char barco) {
switch (barco) {
case FRAGATA_1:
bitSet(leds, 0);
break;
case FRAGATA_2:
bitSet(leds, 1);
break;
case DESTRUCTOR_1:
bitSet(leds, 2);
break;
case DESTRUCTOR_2:
bitSet(leds, 3);
break;
case ACORAZADO_1:
bitSet(leds, 4);
break;
case SUBMARINO_1:
bitSet(leds, 5);
break;
case PORTA_AVIONES_1:
bitSet(leds, 6);
break;
}
refrescarLeds();
}
void apagarLedDe(char barco) {
switch (barco) {
case FRAGATA_1:
bitClear(leds, 0);
break;
case FRAGATA_2:
bitClear(leds, 1);
break;
case DESTRUCTOR_1:
bitClear(leds, 2);
break;
case DESTRUCTOR_2:
bitClear(leds, 3);
break;
case ACORAZADO_1:
bitClear(leds, 4);
break;
case SUBMARINO_1:
bitClear(leds, 5);
break;
case PORTA_AVIONES_1:
bitClear(leds, 6);
break;
}
refrescarLeds();
}
boolean hayMasInstanciasDe(char barco) {
/*
Buscar si hay más "instancias" del barco
al que le han disparado. Es decir, si el barco
atacado es, por ejemplo, FRAGATA_1 (que ocupa un espacio),
su espacio será sustituido por un disparo, por lo que no
habrá más FRAGATA_1 en el array
En el caso del submarino, que mide 3, si le dan a una parte
del mismo sólo habrán eliminado 1 espacio, pero en el
arreglo quedarán 2 instancias del mismo, por lo que no se
considerará eliminado
*/
for (int e = 0; e < ANCHURA_LCD; e++) {
for (int n = 0; n < ALTURA_LCD; n++) {
if (escenario[e][n] == barco) return true;
}
}
return false;
}
void establecerPines() {
pinMode(PIN_CLOCK, OUTPUT);
pinMode(PIN_LATCH, OUTPUT);
pinMode(PIN_DATA, OUTPUT);
pinMode(BUZZER, OUTPUT);
}
void encenderBuzzer() {
digitalWrite(BUZZER, HIGH);
}
void apagarBuzzer() {
digitalWrite(BUZZER, LOW);
}
void marcarDisparoNoAcertadoEn(int x, int y) {
escenario[x][y] = DISPARO_NO_ACERTADO;
}
void marcarDisparoAcertadoEn(int x, int y) {
escenario[x][y] = DISPARO_ACERTADO;
}
void indicarDisparoAcertado() {
encenderBuzzer();
delay(DURACION_BUZZER_DISPARO_ACERTADO);
apagarBuzzer();
}
void indicarDisparoErroneo() {
for (int x = 0; x < 5; x++) {
encenderBuzzer();
delay((DURACION_BUZZER_DISPARO_NO_ACERTADO / 2) / 5);
apagarBuzzer();
delay((DURACION_BUZZER_DISPARO_NO_ACERTADO / 2) / 5);
}
}
char dispararYObtenerObjetivo(int x, int y) {
return escenario[x][y];
}
void escucharJugadorDos() {
serialJugadorDos.listen();
}
boolean estaVacioEn(int x, int y) {
return x >= 0 && x < ANCHURA_LCD && y >= 0 && y < ALTURA_LCD && escenario[x][y] == AGUA;
}
char queHayEn(int x, int y) {
return escenario[x][y];
}
void pedirUbicacionDeBarcos() {
serialJugadorUno.listen();
serialJugadorUno.println("Bienvenido, jugador 1. Es hora de ubicar tus barcos");
String coordenadas;
int barcos = 7,
barcosCompletados = 0;
do {
String nombreBarco = "";
switch (barcosCompletados) {
case 0:
nombreBarco = "Fragata 1";
break;
case 1:
nombreBarco = "Fragata 2";
break;
case 2:
nombreBarco = "Destructor 1";
break;
case 3:
nombreBarco = "Destructor 2";
break;
case 4:
nombreBarco = "Acorazado";
break;
case 5:
nombreBarco = "Submarino";
break;
case 6:
nombreBarco = "Porta aviones";
break;
}
serialJugadorUno.println("Introduce las coordenadas de " + nombreBarco + "");
while (!serialJugadorUno.available());
coordenadas = serialJugadorUno.readString();
int x, y;
if (esCoordenadaValida(coordenadas, x, y)) {
switch (barcosCompletados) {
case 0:
//Fragata 1
if (intentarDibujarBarco(x, y, FRAGATA_1, LONGITUD_FRAGATAS)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(FRAGATA_1);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
case 1:
//Fragata 2
if (intentarDibujarBarco(x, y, FRAGATA_2, LONGITUD_FRAGATAS)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(FRAGATA_2);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
case 2:
//Destructor 1
if (intentarDibujarBarco(x, y, DESTRUCTOR_1, LONGITUD_DESTRUCTORES)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(DESTRUCTOR_1);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
case 3:
//Destructor 2
if (intentarDibujarBarco(x, y, DESTRUCTOR_2, LONGITUD_DESTRUCTORES)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(DESTRUCTOR_2);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
case 4:
//Acorazado 1
if (intentarDibujarBarco(x, y, ACORAZADO_1, LONGITUD_ACORAZADOS)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(ACORAZADO_1);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
case 5:
//Submarino 1
if (intentarDibujarBarco(x, y, SUBMARINO_1, LONGITUD_SUBMARINOS)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(SUBMARINO_1);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
case 6:
//Porta aviones 1
if (intentarDibujarBarco(x, y, PORTA_AVIONES_1, LONGITUD_PORTA_AVIONES)) {
dibujarEscenario();
barcosCompletados++;
encenderLedDe(PORTA_AVIONES_1);
} else {
serialJugadorUno.println("Coordenadas correctas, pero ya existe un barco en esa posición. Intenta de nuevo");
}
break;
}
} else {
serialJugadorUno.println("Coordenadas incorrectas o fuera de rango. Recuerda que debes introducirlas en el formato x,y (con la coma incluida)");
}
coordenadas = "";
} while (barcosCompletados < barcos);
serialJugadorUno.println("Se ha terminado tu tiempo. Turno del jugador 2");
}
boolean intentarDibujarBarco(int x, int y, char simbolo, int longitud) {
boolean puedeDibujar = true;
//A la derecha
for (int d = 0; d < longitud; d++) {
puedeDibujar = puedeDibujar && estaVacioEn(x + d, y);
}
if (puedeDibujar) {
for (int d = 0; d < longitud; d++) {
escenario[x + d][y] = simbolo;
}
return true;
}
//Hacia abajo
puedeDibujar = true;
for (int d = 0; d < longitud; d++) {
puedeDibujar = puedeDibujar && estaVacioEn(x, y + d);
}
if (puedeDibujar) {
for (int d = 0; d < longitud; d++) {
escenario[x][y + d] = simbolo;
}
return true;
}
//Hacia la izquierda
puedeDibujar = true;
for (int d = 0; d < longitud; d++) {
puedeDibujar = puedeDibujar && estaVacioEn(x - d, y);
}
if (puedeDibujar) {
for (int d = 0; d < longitud; d++) {
escenario[x - d][y] = simbolo;
}
return true;
}
//Hacia arriba
puedeDibujar = true;
for (int d = 0; d < longitud; d++) {
puedeDibujar = puedeDibujar && estaVacioEn(x, y - d);
}
if (puedeDibujar) {
for (int d = 0; d < longitud; d++) {
escenario[x][y - d] = simbolo;
}
return true;
}
return false;
}
boolean isNumeric(String str) {
/*
Gracias a http://tripsintech.com/arduino-isnumeric-function/
*/
unsigned int stringLength = str.length();
if (stringLength == 0) {
return false;
}
boolean seenDecimal = false;
for (unsigned int i = 0; i < stringLength; ++i) {
if (isDigit(str.charAt(i))) {
continue;
}
if (str.charAt(i) == '.') {
if (seenDecimal) {
return false;
}
seenDecimal = true;
continue;
}
return false;
}
return true;
}
boolean esCoordenadaValida(String coordenadas, int &x, int &y) {
int indice = coordenadas.indexOf(",");
if (indice != -1) {
String supuestoX = coordenadas.substring(0, indice),
supuestoY = coordenadas.substring(indice + 1, coordenadas.length() - 2);
if (isNumeric(supuestoX) && isNumeric(supuestoY)) {
x = supuestoX.toInt(), y = supuestoY.toInt();
return x >= 0 && y >= 0 && x < ANCHURA_LCD && y < ALTURA_LCD;
}
}
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment