#include <Adafruit_NeoPixel.h>
#include <Vector.h>
#include <limits.h>
#define PIN 4
#define RGB 4
#define SIZE 16
#define SHIP 10
#define SLEEP 30
#define SPEED 200
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;
};
bool error = false;
bool selected = false;
Coord c;
Coord storage_v[2 * SHIP] = {};
Vector<Coord> v(storage_v, 2 * SHIP);
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}};
void setup() {
Serial.begin(9600);
pixels.begin();
//Clear vectors initialized with zeroes
v.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)
}
//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)
//Clear game field (for random set only)
void ClearGameField() {
for (int i = 0; i < v.size(); i++) {
pixels.setPixelColor(XY(v[i].y, v[i].x), 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(XY(y - 2, x - 2)) != 0) ||
(y - 2 >= 0 && x - 1 >= 0 && pixels.getPixelColor(XY(y - 2, x - 1)) != 0) ||
(y - 2 >= 0 && x >= 0 && x < SIZE && pixels.getPixelColor(XY(y - 2, x)) != 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(XY(cy, cx - 1)) != 0) ||
(cy >= 0 && cy < SIZE && cx + 1 < SIZE && pixels.getPixelColor(XY(cy, cx + 1)) != 0)) {
return false;
}
}
if ((y + decks - 1 >= 0 && y + decks - 1 < SIZE && x - 2 >= 0 && pixels.getPixelColor(XY(y + decks - 1, x - 2)) != 0) ||
(y + decks - 1 >= 0 && y + decks - 1 < SIZE && x - 1 >= 0 && pixels.getPixelColor(XY(y + decks - 1, x - 1)) != 0) ||
(y + decks - 1 >= 0 && y + decks - 1 < SIZE && x >= 0 && x < SIZE && pixels.getPixelColor(XY(y + decks - 1, x)) != 0)) {
return false;
}
return true;
} else if (dir == "VER") {
if ((y - 2 >= 0 && x - 2 >= 0 && pixels.getPixelColor(XY(y - 2, x - 2)) != 0) ||
(y - 1 >= 0 && x - 2 >= 0 && pixels.getPixelColor(XY(y - 1, x - 2)) != 0) ||
(y >= 0 && y < SIZE && x - 2 >= 0 && pixels.getPixelColor(XY(y, x - 2)) != 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(XY(cy - 1, cx)) != 0) ||
(cy + 1 < SIZE && cx >= 0 && cx < SIZE && pixels.getPixelColor(XY(cy + 1, cx)) != 0)) {
return false;
}
}
if ((y - 2 >= 0 && x + decks - 1 >= 0 && x + decks - 1 < SIZE && pixels.getPixelColor(XY(y - 2, x + decks - 1)) != 0) ||
(y - 1 >= 0 && x + decks - 1 >= 0 && x + decks - 1 < SIZE && pixels.getPixelColor(XY(y - 1, x + decks - 1)) != 0) ||
(y >= 0 && y < SIZE && x + decks - 1 >= 0 && x + decks - 1 < SIZE && pixels.getPixelColor(XY(y, x + decks - 1)) != 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(XY(v.back().y, v.back().x), 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(XY(c.y, c.x), 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
void PlayerJoystickControl() {
GenerateRandomShipSet();
delay(SPEED);
}
//Set a player joystick control
//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();
delay(3000);
}
byte XY(byte x, byte y) {
return y * SIZE + x;
}