//Copyright (C) 29.04.2025, Kirill ZHivotkov
#include <Adafruit_NeoPixel.h>
#include <Vector.h>
#include <limits.h>
#define PIN 4
#define RGB 4
#define SIZE 16
#define SHIP 10
#define MOVE 512
#define SLEEP 30
#define SPEED 200
#define BUZZER 7
#define YELLOW 16776960
#define RED 16711680
#define ORANGE 16744192
#define GREEN 65407
#define BLUE 255
#define VIOLET 8323327
#define CYAN 65535
#define XJ1 A0 //Arduino pin connected to RX pin of joystick 1
#define YJ1 A1 //Arduino pin connected to RY pin of joystick 1
#define SW1 A2 //Arduino pin connected to SW pin of joystick 1
Adafruit_NeoPixel pixels(SIZE * SIZE, PIN);
struct Color {
byte r;
byte g;
byte b;
};
struct Coord {
byte x;
byte y;
Color c;
};
struct Deck {
byte qty;
byte slot;
};
byte p_x = 0;
byte p_y = 0;
String shipManualSetDir = "";
byte shipManualSetDecks = 0;
bool error = false;
bool selected = false;
bool shipAcceptedManually = true;
Coord c;
Coord storage_v[2 * SHIP] = {};
Vector<Coord> v(storage_v, 2 * SHIP);
Coord storage_p[RGB] = {};
Vector<Coord> p(storage_p, RGB);
String direction[SHIP] = {"HOR", "VER", "HOR", "VER", "HOR", "VER", "HOR", "VER", "HOR", "VER"};
Deck decks[SHIP] = {{4, 0}, {3, 0}, {3, 0}, {2, 0}, {2, 0}, {2, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}};
Color colors[RGB] = {{UCHAR_MAX / 2, 0, UCHAR_MAX}, {0, UCHAR_MAX, UCHAR_MAX}, {0, 0, UCHAR_MAX}, {0, UCHAR_MAX, UCHAR_MAX / 2}};
//Color colors[RGB] = {{UCHAR_MAX, UCHAR_MAX, 0}, {UCHAR_MAX, UCHAR_MAX, 0}, {UCHAR_MAX, UCHAR_MAX, 0}, {UCHAR_MAX, UCHAR_MAX, 0}};
void setup() {
Serial.begin(9600);
pixels.begin();
//Link buzzer
pinMode(BUZZER, OUTPUT);
//Link buzzer
//Link joystick
pinMode(XJ1, INPUT);
pinMode(YJ1, INPUT);
pinMode(SW1, INPUT);
//Link joystick
//Clear vectors initialized with zeroes
v.clear();
p.clear();
//Clear vectors initialized with zeroes
//Shuffle decks and colors (primarily for the random set)
ShuffleColors(decks, SHIP);
ShuffleColors(colors, RGB);
//Shuffle decks and colors (primarily for the random set)
//Set the initial led position (orange one in the left top corner)
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX / 2, 0));
pixels.show();
//Set the initial led position (orange one in the left top corner)
}
//Shuffle decks and their colors (template function)
template<class T>
void ShuffleColors(T arr[], byte limit) {
for (byte i = 0; i < limit; i++) {
byte n = random(0, limit);
auto temp = arr[n];
arr[n] = arr[i];
arr[i] = temp;
}
}
//Shuffle decks and their colors (template function)
//Set a color for the manually set ship (for manual set only)
void SetManuallySetShipColor() {
shipManualSetDir = "";
shipManualSetDecks = 0;
//byte count[RGB]{0};
bool freeShipSlotIsFound = false;
for (int i = 0; i < SHIP; i++) {
if (decks[i].qty == p.size() && decks[i].slot == 0) {
decks[i].slot = 1;
freeShipSlotIsFound = true;
break;
}
}
if (freeShipSlotIsFound == true && p.size() > 0) {
for (int i = 0; i < p.size(); i++) {
c.c.r = colors[RGB - p.size()].r;
c.c.g = colors[RGB - p.size()].g;
c.c.b = colors[RGB - p.size()].b;
pixels.setPixelColor(p[i].y + p[i].x * SIZE, pixels.Color(c.c.r, c.c.g, c.c.b));
pixels.show();
}
p.clear();
tone(BUZZER, 200, 1000);
} else {
if (p.size() > 0) {
for (int i = 0; i < p.size(); i++) {
pixels.setPixelColor(p[i].y + p[i].x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
}
bool allShipsAreSetCorrectly = true;
for (int i = 0; i < SHIP; i++) {
if (decks[i].slot == 0) {
allShipsAreSetCorrectly = false;
}
}
if (allShipsAreSetCorrectly == false) { //There are still some ships to set
Serial.print(p.size());
Serial.println(" DECK(S) SHIPS WERE ALREADY SET!");
/*
for (int i = 0; i < SHIP; i++) {
if (decks[i].qty == RGB && decks[i].slot == 1) {
count[RGB-1]++;
} else if (decks[i].qty == RGB - 1 && decks[i].slot == 1) {
count[RGB-2]++;
} else if (decks[i].qty == RGB - 2 && decks[i].slot == 1) {
count[RGB-3]++;
} else if (decks[i].qty == RGB - 3 && decks[i].slot == 1) {
count[RGB-4]++;
}
}
*/
p.clear();
tone(BUZZER, 500, 1000);
} else { //All ships are set correctly
p.clear();
tone(BUZZER, 50, 1000);
delay(100);
tone(BUZZER, 300, 1000);
delay(100);
tone(BUZZER, 450, 1000);
delay(100);
Serial.println("ALL THE SHIPS HAVE BEEN SET!");
}
}
}
}
//Set a color for the manually set ship (for manual set only)
//Check that a ship may be manually set (for manual set only)
void CheckManualShipSetEachStep() {
//Define a direction for the ship
if (shipManualSetDir == "") {
if ((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE && pixels.getPixelColor((p_y - 1) + p_x * SIZE) == YELLOW) || (p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE && pixels.getPixelColor((p_y + 1) + p_x * SIZE) == YELLOW)) {
shipManualSetDir = "HOR";
shipManualSetDecks++;
} else if ((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor(p_y + (p_x - 1) * SIZE) == YELLOW) || (p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor(p_y + (p_x + 1) * SIZE) == YELLOW)) {
shipManualSetDir = "VER";
shipManualSetDecks++;
}
}
//Define a direction for the ship
//Act depending on the previously defined direction
if (shipManualSetDir == "HOR") {
if (shipManualSetDecks < RGB) { //If there are not more than 4 decks
if ((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE && pixels.getPixelColor((p_y - 1) + p_x * SIZE) == YELLOW) || (p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE && pixels.getPixelColor((p_y + 1) + p_x * SIZE) == YELLOW)) {
shipAcceptedManually = true;
shipManualSetDecks++;
} else if ((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor(p_y + (p_x - 1) * SIZE) == YELLOW) || (p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor(p_y + (p_x + 1) * SIZE) == YELLOW)) {
shipAcceptedManually = false;
}
} else { //If a ship has more than 4 decks
shipAcceptedManually = false;
}
} else if (shipManualSetDir == "VER") {
if (shipManualSetDecks < RGB) { //If there are not more than 4 decks
if (pixels.getPixelColor(p_y + (p_x - 1) * SIZE) == YELLOW || pixels.getPixelColor(p_y + (p_x + 1) * SIZE) == YELLOW) {
shipAcceptedManually = true;
shipManualSetDecks++;
} else if (pixels.getPixelColor((p_y - 1) + p_x * SIZE) == YELLOW || pixels.getPixelColor((p_y + 1) + p_x * SIZE) == YELLOW) {
shipAcceptedManually = false;
}
} else { //If a ship has more than 4 decks
shipAcceptedManually = false;
}
}
//Act depending on the previously defined direction
//Check if there are no yellow decks on the diagonals
if ((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor((p_y - 1) + (p_x - 1) * SIZE) == YELLOW) ||
(p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor((p_y - 1) + (p_x + 1) * SIZE) == YELLOW) ||
(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor((p_y + 1) + (p_x - 1) * SIZE) == YELLOW) ||
(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor((p_y + 1) + (p_x + 1) * SIZE) == YELLOW)) {
shipAcceptedManually = false;
}
//Check if there are no yellow decks on the diagonals
//Check (already set) ship nearby
if (((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x - 1) * SIZE) == GREEN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x) * SIZE) == GREEN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x + 1) * SIZE) == GREEN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x - 1) * SIZE) == GREEN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x) * SIZE) == GREEN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x + 1) * SIZE) == GREEN)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x - 1) * SIZE) == GREEN)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x + 1) * SIZE) == GREEN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x - 1) * SIZE) == BLUE)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x) * SIZE) == BLUE)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x + 1) * SIZE) == BLUE)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x - 1) * SIZE) == BLUE)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x) * SIZE) == BLUE)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x + 1) * SIZE) == BLUE)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x - 1) * SIZE) == BLUE)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x + 1) * SIZE) == BLUE)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x - 1) * SIZE) == VIOLET)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x) * SIZE) == VIOLET)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x + 1) * SIZE) == VIOLET)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x - 1) * SIZE) == VIOLET)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x) * SIZE) == VIOLET)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x + 1) * SIZE) == VIOLET)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x - 1) * SIZE) == VIOLET)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x + 1) * SIZE) == VIOLET)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x - 1) * SIZE) == CYAN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x) * SIZE) == CYAN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y - 1) + (p_x + 1) * SIZE) == CYAN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x - 1) * SIZE) == CYAN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x) * SIZE) == CYAN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y + 1) + (p_x + 1) * SIZE) == CYAN)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x - 1) * SIZE) == CYAN)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor((p_y) + (p_x + 1) * SIZE) == CYAN))) {
shipAcceptedManually = false;
SetManuallySetShipColor();
}
//Check (already set) ship nearby
//Allow manual ship set
if ((!(p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) || pixels.getPixelColor((p_y - 1) + (p_x - 1) * SIZE) == 0) &&
(!(p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) || pixels.getPixelColor((p_y - 1) + (p_x) * SIZE) == 0) &&
(!(p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) || pixels.getPixelColor((p_y - 1) + (p_x + 1) * SIZE) == 0) &&
(!(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) || pixels.getPixelColor((p_y + 1) + (p_x - 1) * SIZE) == 0) &&
(!(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) || pixels.getPixelColor((p_y + 1) + (p_x) * SIZE) == 0) &&
(!(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) || pixels.getPixelColor((p_y + 1) + (p_x + 1) * SIZE) == 0) &&
(!(p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) || pixels.getPixelColor((p_y) + (p_x - 1) * SIZE) == 0) &&
(!(p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) || pixels.getPixelColor((p_y) + (p_x + 1) * SIZE) == 0)) {
shipAcceptedManually = true;
SetManuallySetShipColor();
}
//Allow manual ship set
}
//Check that a ship may be manually set (for manual set only)
//Check if all the decks should be set with a red color when the ship is destroyed
void SetDestroyedShipColor() {
//Init function variables
bool destroyed = true;
byte s_x = UCHAR_MAX;
byte s_y = UCHAR_MAX;
byte decks = 0;
byte k = 0;
//Init function variables
//Horizontal traversal
for (byte i = 0; i < SIZE; i++) {
for (byte j = 0; j < SIZE; j++) {
if (j + 1 < SIZE && pixels.getPixelColor(j + i * SIZE) != 0 && pixels.getPixelColor((j + 1) + i * SIZE) != 0) {
k++;
if (k == 1) {
s_x = i;
s_y = j;
}
} else {
if (k > 0) { //If the ship has more than 1 deck
decks = k;
//Destroyed
for (int p = 0; p <= decks; p++) {
//Serial.print(s_x);
//Serial.print(" | ");
//Serial.println(s_y + p);
if ((s_x >= 0 && s_x < SIZE && s_y + p >= 0 && s_y + p < SIZE && pixels.getPixelColor((s_y + p) + s_x * SIZE) != YELLOW) || (s_x == UCHAR_MAX && s_y == UCHAR_MAX)) {
//Serial.println("THE HORIZONTAL SHIP MAY NOT BE DESTROYED!");
destroyed = false;
}
}
if (destroyed == true) {
Serial.println("THE HORIZONTAL SHIP WAS DESTROYED!");
for (int p = 0; p <= decks; p++) {
if (s_x >= 0 && s_x < SIZE && s_y + p >= 0 && s_y + p < SIZE) {
Serial.print(s_x);
Serial.print(" | ");
Serial.println(s_y + p);
pixels.setPixelColor(((s_y + p) + s_x * SIZE), pixels.Color(UCHAR_MAX, 0, 0));
pixels.show();
}
}
}
//Destroyed
k = 0;
destroyed = true;
} else { //If the ship has only 1 deck
if (i >= 0 && i < SIZE && j >= 0 && j < SIZE && pixels.getPixelColor(j + i * SIZE) == YELLOW && pixels.getPixelColor(j + (i - 1) * SIZE) == 0 && pixels.getPixelColor(j + (i + 1) * SIZE) == 0) {
Serial.println("THE HORIZONTAL SHIP WAS DESTROYED!");
Serial.print(i);
Serial.print(" | ");
Serial.println(j);
pixels.setPixelColor((j + i * SIZE), pixels.Color(UCHAR_MAX, 0, 0));
pixels.show();
k = 0;
destroyed = true;
}
}
}
}
}
//Horizontal traversal
//Reset function variables
destroyed = true;
s_x = UCHAR_MAX;
s_y = UCHAR_MAX;
decks = k = 0;
//Reset function variables
//Vertical traversal
for (byte j = 0; j < SIZE; j++) {
for (byte i = 0; i < SIZE; i++) {
if (i + 1 < SIZE && pixels.getPixelColor(j + i * SIZE) != 0 && pixels.getPixelColor(j + (i + 1) * SIZE) != 0) {
k++;
if (k == 1) {
s_x = i;
s_y = j;
}
} else {
if (k > 0) { //If the ship has more than 1 deck
decks = k;
//Destroyed
for (int p = 0; p <= decks; p++) {
//Serial.print(s_x + p);
//Serial.print(" | ");
//Serial.println(s_y);
if ((s_y >= 0 && s_y < SIZE && s_x + p >= 0 && s_x + p < SIZE && pixels.getPixelColor(s_y + (s_x + p) * SIZE) != YELLOW) || (s_x == UCHAR_MAX && s_y == UCHAR_MAX)) {
//Serial.println("THE VERTICAL SHIP MAY NOT BE DESTROYED!");
destroyed = false;
}
}
if (destroyed == true) {
Serial.println("THE VERTICAL SHIP WAS DESTROYED!");
for (int p = 0; p <= decks; p++) {
if (s_y >= 0 && s_y < SIZE && s_x + p >= 0 && s_x + p < SIZE) {
Serial.print(s_x + p);
Serial.print(" | ");
Serial.println(s_y);
pixels.setPixelColor((s_y + (s_x + p) * SIZE), pixels.Color(UCHAR_MAX, 0, 0));
pixels.show();
}
}
}
//Destroyed
k = 0;
destroyed = true;
} else { //If the ship has only 1 deck
if (i >= 0 && i < SIZE && j >= 0 && j < SIZE && pixels.getPixelColor(j + i * SIZE) == YELLOW && pixels.getPixelColor((j - 1) + i * SIZE) == 0 && pixels.getPixelColor((j + 1) + i * SIZE) == 0) {
Serial.println("THE VERTICAL SHIP WAS DESTROYED!");
Serial.print(i);
Serial.print(" | ");
Serial.println(j);
pixels.setPixelColor((j + i * SIZE), pixels.Color(UCHAR_MAX, 0, 0));
pixels.show();
k = 0;
destroyed = true;
}
}
}
}
}
//Vertical traversal
}
//Check if all the decks should be set with a red color when the ship is destroyed
//Clear game field (for random set only)
void ClearGameField() {
for (int i = 0; i < v.size(); i++) {
pixels.setPixelColor(v[i].y + v[i].x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
}
}
//Clear game field (for random set only)
//Check if the ship is not beyond the grid (for random set only)
bool CheckFieldShipBoundary(byte x, byte y, byte decks, String dir, String name) {
if ((x >= 1 && x <= SIZE) && (y >= 1 && y <= SIZE)) {
if (dir == "HOR") {
if (decks - 1 <= SIZE - y) {
return true;
}
return false;
} else if (dir == "VER") {
if (decks - 1 <= SIZE - x) {
return true;
}
return false;
}
}
return false;
}
//Check if the ship is not beyond the grid (for random set only)
//Check if the ship has got enough space around (for random set only)
bool CheckShipObstacleAround(byte x, byte y, byte decks, String dir, String name) {
if (dir == "HOR") {
if ((y - 2 >= 0 && x - 2 >= 0 && pixels.getPixelColor((y - 2) + (x - 2) * SIZE) != 0) ||
(y - 2 >= 0 && x - 1 >= 0 && pixels.getPixelColor((y - 2) + (x - 1) * SIZE) != 0) ||
(y - 2 >= 0 && x >= 0 && x < SIZE && pixels.getPixelColor((y - 2) + (x) * SIZE) != 0)) {
return false;
}
for (byte i = 0; i < decks; i++) {
byte cx = x - 1;
byte cy = y - 1 + i;
if ((cy >= 0 && cy < SIZE && cx - 1 >= 0 && pixels.getPixelColor(cy + (cx - 1) * SIZE) != 0) ||
(cy >= 0 && cy < SIZE && cx + 1 < SIZE && pixels.getPixelColor(cy + (cx + 1) * SIZE) != 0)) {
return false;
}
}
if ((y + decks - 1 >= 0 && y + decks - 1 < SIZE && x - 2 >= 0 && pixels.getPixelColor((y + decks - 1) + (x - 2) * SIZE) != 0) ||
(y + decks - 1 >= 0 && y + decks - 1 < SIZE && x - 1 >= 0 && pixels.getPixelColor((y + decks - 1) + (x - 1) * SIZE) != 0) ||
(y + decks - 1 >= 0 && y + decks - 1 < SIZE && x >= 0 && x < SIZE && pixels.getPixelColor((y + decks - 1) + (x) * SIZE) != 0)) {
return false;
}
return true;
} else if (dir == "VER") {
if ((y - 2 >= 0 && x - 2 >= 0 && pixels.getPixelColor((y - 2) + (x - 2) * SIZE) != 0) ||
(y - 1 >= 0 && x - 2 >= 0 && pixels.getPixelColor((y - 1) + (x - 2) * SIZE) != 0) ||
(y >= 0 && y < SIZE && x - 2 >= 0 && pixels.getPixelColor((y) + (x - 2) * SIZE) != 0)) {
return false;
}
for (byte i = 0; i < decks; i++) {
byte cx = x - 1 + i;
byte cy = y - 1;
if ((cy - 1 >= 0 && cx >= 0 && cx < SIZE && pixels.getPixelColor((cy - 1) + cx * SIZE) != 0) ||
(cy + 1 < SIZE && cx >= 0 && cx < SIZE && pixels.getPixelColor((cy + 1) + cx * SIZE) != 0)) {
return false;
}
}
if ((y - 2 >= 0 && x + decks - 1 >= 0 && x + decks - 1 < SIZE && pixels.getPixelColor((y - 2) + (x + decks - 1) * SIZE) != 0) ||
(y - 1 >= 0 && x + decks - 1 >= 0 && x + decks - 1 < SIZE && pixels.getPixelColor((y - 1) + (x + decks - 1) * SIZE) != 0) ||
(y >= 0 && y < SIZE && x + decks - 1 >= 0 && x + decks - 1 < SIZE && pixels.getPixelColor((y) + (x + decks - 1) * SIZE) != 0)) {
return false;
}
return true;
}
}
//Check if the ship has got enough space around (for random set only)
//Check if one ship does not overlap another (for random set only)
void ResetShipAfterOverlap(int i, int cx, int cy, String name) {
for (byte j = 0; j < v.size(); j++) {
if ((v[j].x == cx) && (v[j].y == cy)) {
Serial.println("OVERLAPPED " + name + "!");
delay(SLEEP);
byte index = i;
for (byte i = 0; i < index; i++) {
pixels.setPixelColor(v.back().y + v.back().x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
v.pop_back();
}
error = true;
break;
}
}
}
//Check if one ship does not overlap another (for random set only)
//Check random ship RGB color (the same number of decks) (for random set only)
void SetShipRGBColor(byte x, byte y, byte decks, String dir, String name) {
c.x = x;
c.y = y;
for (int i = 0; i < RGB; i++) {
if (decks == RGB - i) {
c.c.r = colors[decks-1].r;
c.c.g = colors[decks-1].g;
c.c.b = colors[decks-1].b;
}
}
v.push_back(c);
pixels.setPixelColor((c.y + c.x * SIZE), pixels.Color(c.c.r, c.c.g, c.c.b));
pixels.show();
}
//Check random ship RGB color (the same number of decks) (for random set only)
//Set a random ship position (for random set only)
void SetHomeShipPosition(byte x, byte y, byte decks, String dir, String name) {
error = false;
if ((CheckFieldShipBoundary(x, y, decks, dir, name) == true) && (CheckShipObstacleAround(x, y, decks, dir, name) == true)) {
if (dir == "HOR") {
for (byte i = 0; i < decks; i++) {
byte cx = x - 1;
byte cy = y - 1 + i;
ResetShipAfterOverlap(i, cx, cy, name);
if (error == true) {
break;
}
SetShipRGBColor(cx, cy, decks, dir, name);
}
if (error == false) {
Serial.println("OK " + name + "!");
delay(SLEEP);
}
} else if (dir == "VER") {
for (byte i = 0; i < decks; i++) {
byte cx = x - 1 + i;
byte cy = y - 1;
ResetShipAfterOverlap(i, cx, cy, name);
if (error == true) {
break;
}
SetShipRGBColor(cx, cy, decks, dir, name);
}
if (error == false) {
Serial.println("OK " + name + "!");
delay(SLEEP);
}
}
} else {
if (CheckFieldShipBoundary(x, y, decks, dir, name) == false) {
error = true;
Serial.println("NOT ENOUGH SPACE " + name + "!");
delay(SLEEP);
} else if (CheckShipObstacleAround(x, y, decks, dir, name) == false) {
error = true;
Serial.println("TOO NEARBY " + name + "!");
delay(SLEEP);
}
}
}
//Set a random ship position (for random set only)
//Set a player joystick control (testing purposes)
void PlayerJoystickControl() {
/*
//If a random distrubution on the field is selected by the player
if ((analogRead(SW1) == 0) && (selected == false) && (v.size() == 2 * SHIP)) {
//Serial.println("SELECTED");
selected = true;
}
//If a random distrubution on the field is selected by the player
*/
//If a random distribution (or manual set) on the field is selected by the player
if ((analogRead(SW1) == 0) && selected == false) {
if (pixels.getPixelColor(p_y + p_x * SIZE) == ORANGE) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX, 0));
pixels.show();
CheckManualShipSetEachStep();
if (shipAcceptedManually == false) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, 0, 0));
pixels.show();
tone(BUZZER, 100, 1000);
} else {
p.push_back({p_x, p_y, {}});
}
selected = true;
}
}
//If a random distribution (or manual set) on the field is selected by the player
//Let the player choose a random distribution
//if (selected == false) {
int x = analogRead(A1);
int y = analogRead(A0);
if (x == 2 * MOVE - 1 && y == MOVE) { //LEFT
//GenerateRandomShipSet();
if (p_y - 1 >= 0) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(p_y + p_x * SIZE) != YELLOW &&
pixels.getPixelColor(p_y + p_x * SIZE) != GREEN &&
pixels.getPixelColor(p_y + p_x * SIZE) != BLUE &&
pixels.getPixelColor(p_y + p_x * SIZE) != VIOLET &&
pixels.getPixelColor(p_y + p_x * SIZE) != CYAN) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
}
p_y--;
if (pixels.getPixelColor(p_y + p_x * SIZE) == 0) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX / 2, 0));
pixels.show();
}
}
delay(SPEED);
} else if (x == 0 && y == MOVE) { //RIGHT
//GenerateRandomShipSet();
if (p_y + 1 < SIZE) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(p_y + p_x * SIZE) != YELLOW &&
pixels.getPixelColor(p_y + p_x * SIZE) != GREEN &&
pixels.getPixelColor(p_y + p_x * SIZE) != BLUE &&
pixels.getPixelColor(p_y + p_x * SIZE) != VIOLET &&
pixels.getPixelColor(p_y + p_x * SIZE) != CYAN) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
}
p_y++;
if (pixels.getPixelColor(p_y + p_x * SIZE) == 0) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX / 2, 0));
pixels.show();
}
}
delay(SPEED);
} else if (y == 2 * MOVE - 1 && x == MOVE) { //DOWN
//GenerateRandomShipSet();
if (p_x + 1 < SIZE) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(p_y + p_x * SIZE) != YELLOW &&
pixels.getPixelColor(p_y + p_x * SIZE) != GREEN &&
pixels.getPixelColor(p_y + p_x * SIZE) != BLUE &&
pixels.getPixelColor(p_y + p_x * SIZE) != VIOLET &&
pixels.getPixelColor(p_y + p_x * SIZE) != CYAN) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
}
p_x++;
if (pixels.getPixelColor(p_y + p_x * SIZE) == 0) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX / 2, 0));
pixels.show();
}
}
//pixels.setPixelColor(0 + 0 * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX / 2, 0));
//pixels.show();
//Serial.println(pixels.getPixelColor(0 + 0 * SIZE));
/*
pixels.setPixelColor(0 + 0 * SIZE, pixels.Color(0, UCHAR_MAX, UCHAR_MAX / 2));
pixels.show();
Serial.println(pixels.getPixelColor(0 + 0 * SIZE));
pixels.setPixelColor(1 + 0 * SIZE, pixels.Color(0, 0, UCHAR_MAX));
pixels.show();
Serial.println(pixels.getPixelColor(1 + 0 * SIZE));
pixels.setPixelColor(2 + 0 * SIZE, pixels.Color(UCHAR_MAX / 2, 0, UCHAR_MAX));
pixels.show();
Serial.println(pixels.getPixelColor(2 + 0 * SIZE));
pixels.setPixelColor(3 + 0 * SIZE, pixels.Color(0, UCHAR_MAX, UCHAR_MAX));
pixels.show();
Serial.println(pixels.getPixelColor(3 + 0 * SIZE));
*/
delay(SPEED);
//SetDestroyedShipColor(); //Testing destroyed ship coloring
//selected = true;
} else if (y == 0 && x == MOVE) { //UP
//GenerateRandomShipSet();
if (p_x - 1 >= 0) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(p_y + p_x * SIZE) != YELLOW &&
pixels.getPixelColor(p_y + p_x * SIZE) != GREEN &&
pixels.getPixelColor(p_y + p_x * SIZE) != BLUE &&
pixels.getPixelColor(p_y + p_x * SIZE) != VIOLET &&
pixels.getPixelColor(p_y + p_x * SIZE) != CYAN) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(0, 0, 0));
pixels.show();
}
p_x--;
if (pixels.getPixelColor(p_y + p_x * SIZE) == 0) {
pixels.setPixelColor(p_y + p_x * SIZE, pixels.Color(UCHAR_MAX, UCHAR_MAX / 2, 0));
pixels.show();
}
}
delay(SPEED);
}
//}
//Let the player choose a random distribution
}
//Set a player joystick control (testing purposes)
//Generate a random multi-colored field with ships (according to the Naval Clash game rules)
void GenerateRandomShipSet() {
ClearGameField();
v.clear();
for (byte i = 0; i < SHIP; i++) {
SetHomeShipPosition(random(1, SIZE + 1), random(1, SIZE + 1), decks[i].qty, direction[random(0, SIZE)], String(i + 1));
if (error == true) {
i--;
}
}
ShuffleColors(decks, SHIP);
ShuffleColors(colors, RGB);
}
//Generate a random multi-colored field with ships (according to the Naval Clash game rules)
void loop() {
randomSeed(micros() + analogRead(A0));
PlayerJoystickControl();
}