//Copyright (C) 29.04.2025, Kirill ZHivotkov
/*
Эта программа выполняет ручную расстановку кораблей
в игре "Морской Бой", согласно правилам игры.
Для запуска необходимо запустить симуляцию и нажать на
любое из направлений, определяемых джойстиком (для
подтверждения установки палубы необходимо нажать на
центральную кнопку джойстика). Программа устроена так,
что невозможно ошибиться (то есть, можно поставить
только 4 однопалубных корабля, 3 двухпалубных, 2
трехпалубных и 1 четырехпалубный). Также,
невозможно располагать корабли слишком близко
друг к другу.
*/
#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
#define YJ1 A1
#define SW1 A2
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 selected = false;
bool shipAcceptedManually = true;
Coord c;
Coord storage_p[RGB] = {};
Vector<Coord> p(storage_p, RGB);
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] = {{0, UCHAR_MAX, UCHAR_MAX / 2}, {0, UCHAR_MAX, UCHAR_MAX / 2}, {0, UCHAR_MAX, UCHAR_MAX / 2}, {0, UCHAR_MAX, UCHAR_MAX / 2}};
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
p.clear();
//Clear vectors initialized with zeroes
//Set the initial led position (orange one in the left top corner)
pixels.setPixelColor(XY(p_y, p_x), ORANGE);
pixels.show();
//Set the initial led position (orange one in the left top corner)
}
//Set a color for the manually set ship (for manual set only)
void SetManuallySetShipColor() {
shipManualSetDir = "";
shipManualSetDecks = 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(XY(p[i].y, p[i].x), 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(XY(p[i].y, p[i].x), 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!");
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 CheckManualShipSet() {
//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(XY(p_y - 1, p_x)) == YELLOW) || (p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE && pixels.getPixelColor(XY(p_y + 1, p_x)) == YELLOW)) {
shipManualSetDir = "HOR";
shipManualSetDecks++;
} else if ((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor(XY(p_y, p_x - 1)) == YELLOW) || (p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor(XY(p_y, p_x + 1)) == 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(XY(p_y - 1, p_x)) == YELLOW) || (p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE && pixels.getPixelColor(XY(p_y + 1, p_x)) == YELLOW)) {
shipAcceptedManually = true;
shipManualSetDecks++;
} else if ((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor(XY(p_y, p_x - 1)) == YELLOW) || (p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor(XY(p_y, p_x + 1)) == 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(XY(p_y, p_x - 1)) == YELLOW || pixels.getPixelColor(XY(p_y, p_x + 1)) == YELLOW) {
shipAcceptedManually = true;
shipManualSetDecks++;
} else if (pixels.getPixelColor(XY(p_y - 1, p_x)) == YELLOW || pixels.getPixelColor(XY(p_y + 1, p_x)) == 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(XY(p_y - 1, p_x - 1)) == YELLOW) ||
(p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor(XY(p_y - 1, p_x + 1)) == YELLOW) ||
(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE && pixels.getPixelColor(XY(p_y + 1, p_x - 1)) == YELLOW) ||
(p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE && pixels.getPixelColor(XY(p_y + 1, p_x + 1)) == 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(XY(p_y - 1, p_x - 1)) == GREEN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x)) == GREEN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x + 1)) == GREEN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x - 1)) == GREEN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x)) == GREEN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x + 1)) == GREEN)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x - 1)) == GREEN)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x + 1)) == GREEN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x - 1)) == BLUE)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x)) == BLUE)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x + 1)) == BLUE)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x - 1)) == BLUE)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x)) == BLUE)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x + 1)) == BLUE)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x - 1)) == BLUE)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x + 1)) == BLUE)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x - 1)) == VIOLET)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x)) == VIOLET)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x + 1)) == VIOLET)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x - 1)) == VIOLET)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x)) == VIOLET)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x + 1)) == VIOLET)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x - 1)) == VIOLET)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x + 1)) == VIOLET)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x - 1)) == CYAN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x)) == CYAN)) ||
((p_y - 1 >= 0 && p_y - 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y - 1, p_x + 1)) == CYAN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x - 1)) == CYAN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x >= 0 && p_x < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x)) == CYAN)) ||
((p_y + 1 >= 0 && p_y + 1 < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y + 1, p_x + 1)) == CYAN)) ||
((p_y >= 0 && p_y < SIZE && p_x - 1 >= 0 && p_x - 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x - 1)) == CYAN)) ||
((p_y >= 0 && p_y < SIZE && p_x + 1 >= 0 && p_x + 1 < SIZE) && (pixels.getPixelColor(XY(p_y, p_x + 1)) == 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)
//Set a player joystick control
void PlayerJoystickControl() {
if ((analogRead(SW1) == 0) && selected == false) {
if (pixels.getPixelColor(XY(p_y, p_x)) == ORANGE) {
pixels.setPixelColor(XY(p_y, p_x), YELLOW);
pixels.show();
CheckManualShipSet();
if (shipAcceptedManually == false) {
pixels.setPixelColor(XY(p_y, p_x), RED);
pixels.show();
tone(BUZZER, 100, 1000);
} else {
p.push_back({p_x, p_y, {}});
}
selected = true;
}
}
int x = analogRead(A1);
int y = analogRead(A0);
if (x == 2 * MOVE - 1 && y == MOVE) { //LEFT
if (p_y - 1 >= 0) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(XY(p_y, p_x)) != YELLOW &&
pixels.getPixelColor(XY(p_y, p_x)) != GREEN &&
pixels.getPixelColor(XY(p_y, p_x)) != BLUE &&
pixels.getPixelColor(XY(p_y, p_x)) != VIOLET &&
pixels.getPixelColor(XY(p_y, p_x)) != CYAN) {
pixels.setPixelColor(XY(p_y, p_x), pixels.Color(0, 0, 0));
pixels.show();
}
p_y--;
if (pixels.getPixelColor(XY(p_y, p_x)) == 0) {
pixels.setPixelColor(XY(p_y, p_x), ORANGE);
pixels.show();
}
}
delay(SPEED);
} else if (x == 0 && y == MOVE) { //RIGHT
if (p_y + 1 < SIZE) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(XY(p_y, p_x)) != YELLOW &&
pixels.getPixelColor(XY(p_y, p_x)) != GREEN &&
pixels.getPixelColor(XY(p_y, p_x)) != BLUE &&
pixels.getPixelColor(XY(p_y, p_x)) != VIOLET &&
pixels.getPixelColor(XY(p_y, p_x)) != CYAN) {
pixels.setPixelColor(XY(p_y, p_x), pixels.Color(0, 0, 0));
pixels.show();
}
p_y++;
if (pixels.getPixelColor(XY(p_y, p_x)) == 0) {
pixels.setPixelColor(XY(p_y, p_x), ORANGE);
pixels.show();
}
}
delay(SPEED);
} else if (y == 2 * MOVE - 1 && x == MOVE) { //DOWN
if (p_x + 1 < SIZE) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(XY(p_y, p_x)) != YELLOW &&
pixels.getPixelColor(XY(p_y, p_x)) != GREEN &&
pixels.getPixelColor(XY(p_y, p_x)) != BLUE &&
pixels.getPixelColor(XY(p_y, p_x)) != VIOLET &&
pixels.getPixelColor(XY(p_y, p_x)) != CYAN) {
pixels.setPixelColor(XY(p_y, p_x), pixels.Color(0, 0, 0));
pixels.show();
}
p_x++;
if (pixels.getPixelColor(XY(p_y, p_x)) == 0) {
pixels.setPixelColor(XY(p_y, p_x), ORANGE);
pixels.show();
}
}
delay(SPEED);
} else if (y == 0 && x == MOVE) { //UP
if (p_x - 1 >= 0) {
selected = false;
shipAcceptedManually = true;
if (pixels.getPixelColor(XY(p_y, p_x)) != YELLOW &&
pixels.getPixelColor(XY(p_y, p_x)) != GREEN &&
pixels.getPixelColor(XY(p_y, p_x)) != BLUE &&
pixels.getPixelColor(XY(p_y, p_x)) != VIOLET &&
pixels.getPixelColor(XY(p_y, p_x)) != CYAN) {
pixels.setPixelColor(XY(p_y, p_x), pixels.Color(0, 0, 0));
pixels.show();
}
p_x--;
if (pixels.getPixelColor(XY(p_y, p_x)) == 0) {
pixels.setPixelColor(XY(p_y, p_x), ORANGE);
pixels.show();
}
}
delay(SPEED);
}
}
//Set a player joystick control
void loop() {
PlayerJoystickControl();
}
int XY(int x, int y) {
return y * SIZE + x;
}