#include <SPI.h>
#include <avr/interrupt.h>
#define CLR(x,y) (x&=(~(1<<y)))
#define SET(x,y) (x|=(1<<y))
#define View_Enemy(x,y) ((x&~y)|(x&f1))
#define View_Self(x,y) ((y&f1)|x)
#define View_Placement(x,y) ((~x&y)|(x&~y)|(y&f1))
const int csPin = PB2; // Chip Select pin 10
const int buz1 =PB5;
const int buz2= PB6;
unsigned char f1=0xFF; //ship flicker
unsigned char f2=1; //cursor flicker
int nr_ships[2]={0,0};
bool ver[2]={true,true};
bool currentPlayer=1;
int winner=-1;
enum GamePhase { PLACE_PHASE, BATTLE_PHASE,END_PHASE };
GamePhase gamePhase = PLACE_PHASE;
struct cursor{
int r;
int c;
bool viewSelf;
bool active;
} P_cursor[2];
struct Ship {
int row;
int col;
int length;
bool vertical;
int hits;
} P_ships[2][5];
void timer1_init() {
// Clear control registers
TCCR1A = 0;
TCCR1B = 0;
// Set up timer with prescaler = 64
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TCCR1B |= (1 << CS11) | (1 << CS10); // Prescaler set to 64
// Set OCR1A value for a 50 ms interval
OCR1A = 249999; // (16000000 / (64 * 1)) - 1, approximate calculation
// Enable Timer1 Compare Match A Interrupt
TIMSK1 |= (1 << OCIE1A);
}
void timer2_init() {
// Clear control registers
TCCR2A = 0;
TCCR2B = 0;
// Set up timer with prescaler = 1024
TCCR2A |= (1 << WGM21); // Configure timer 2 for CTC mode
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); // Prescaler set to 1024
// Set OCR2A value for a 1000 ms interval
OCR2A = 155; // (16000000 / (1024 * 1)) / 256 - 1, approximate calculation
// Enable Timer2 Compare Match A Interrupt
TIMSK2 |= (1 << OCIE2A);
}
ISR(TIMER2_COMPA_vect) {
f1=~f1;
}
ISR(TIMER1_COMPA_vect) {
f2^=1;
}
unsigned char LEDS[2][8] = {{0}};
unsigned char Ships[2][8] = {{0}};
void setup() {
timer2_init();
timer1_init();
sei();
Serial.begin(9600);
Serial.println("aha");
SET(DDRB, csPin); // Set the chip select pin as an output
SPI.begin(); // Initialize SPI
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV128); // Slow down the clock for reliability
initMax7219();
for (uint8_t player = 0; player < 2; player++) {
P_cursor[player] = {0, 0, true, true};
}
}
void loop() {
if (gamePhase == PLACE_PHASE) {
displayPlacePhase();
} else {
displayBattlePhase();
}
if (Serial.available() > 0&&winner==-1) {
byte incomingByte = Serial.read();
playerMovement(incomingByte);
Serial.println(incomingByte);
if (gamePhase == PLACE_PHASE) {
if(nr_ships[0] >= 5 && nr_ships[1] >= 5){
gamePhase = BATTLE_PHASE;
updatePlayerTurn();
}
}
}
else if(winner!=-1){
Serial.print("Castigator: ");
Serial.println(winner);
}
delay(100);
}
void initMax7219() {
sendToMaxAll(0x0C, 0x01,0x01); // Normal operation mode
sendToMaxAll(0x09, 0x00,0x00); // No decoding
sendToMaxAll(0x0B, 0x07,0x07); // Scan limit = 7 (all rows)
sendToMaxAll(0x0A, 0x08,0x08); // Intensity (range is 0x00 to 0x0F)
sendToMaxAll(0x0F, 0x00,0x00); // Display test: off
}
void sendToMaxAll(byte address, byte data1,byte data2) {
CLR(PORTB, csPin);
SPI.transfer(address); // Send the address
SPI.transfer(data1);
SPI.transfer(address); // Send the address
SPI.transfer(data2); // Followed by the data
SET(PORTB, csPin);
}
void playerMovement(byte command){
if(gamePhase == PLACE_PHASE){
int length[2]={(8+nr_ships[0])/3,(8+nr_ships[1])/3};
if(nr_ships[0]!=5){
switch(command){
case 'q':{
rotateShip(0,length[0]);
break;
}
case 'w':{
if(checkMovement(0,-1,0))
P_cursor[0].r-=1;
break;
}
case 'e':{
placeShip(0);
break;
}
case 'a':{
if(ver[0]){
if(checkMovement(0,0,1))
P_cursor[0].c+=1;
}
else{
if(checkMovement(0,0,length[0]))
P_cursor[0].c+=1;
}
break;
}
case 's':{
if(ver[0]){
if(checkMovement(0,length[0],0))
P_cursor[0].r+=1;
}
else{
if(checkMovement(0,1,0))
P_cursor[0].r+=1;
}
break;
}
case 'd':{
if(checkMovement(0,0,-1))
P_cursor[0].c-=1;
break;
}
}
}
if(nr_ships[1]!=5){
switch(command){
case 'u':{
rotateShip(1,length[1]);
break;
}
case 'i':{
if(checkMovement(1,-1,0))
P_cursor[1].r-=1;
break;
}
case 'o':{
placeShip(1);
break;
}
case 'j':{
if(ver[1]){
if(checkMovement(1,0,1))
P_cursor[1].c+=1;
}
else{
if(checkMovement(1,0,length[1]))
P_cursor[1].c+=1;
}
break;
}
case 'k':{
if(ver[1]){
if(checkMovement(1,length[1],0))
P_cursor[1].r+=1;
}
else{
if(checkMovement(1,1,0))
P_cursor[1].r+=1;
}
break;
}
case 'l':{
if(checkMovement(1,0,-1))
P_cursor[1].c-=1;
break;
}
}
}
}
else if(gamePhase == BATTLE_PHASE){
switch(command){
case 'q':{
if(currentPlayer!=0)
P_cursor[0].viewSelf=!P_cursor[0].viewSelf;
break;
}
case 'w':{
if(checkMovement(0,-1,0)&¤tPlayer==0)
P_cursor[0].r-=1;
break;
}
case 'e':{
if(currentPlayer==0)
checkHit(0);
break;
}
case 'a':{
if(checkMovement(0,0,1)&¤tPlayer==0)
P_cursor[0].c+=1;
break;
}
case 's':{
if(checkMovement(0,1,0)&¤tPlayer==0)
P_cursor[0].r+=1;
break;
}
case 'd':{
if(checkMovement(0,0,-1)&¤tPlayer==0)
P_cursor[0].c-=1;
break;
}
case 'u':{
if(currentPlayer!=1)
P_cursor[1].viewSelf=!P_cursor[1].viewSelf;
break;
}
case 'i':{
if(checkMovement(1,-1,0)&¤tPlayer==1)
P_cursor[1].r-=1;
break;
}
case 'o':{
if(currentPlayer==1)
checkHit(1);
break;
}
case 'j':{
if(checkMovement(1,0,1)&¤tPlayer==1)
P_cursor[1].c+=1;
break;
}
case 'k':{
if(checkMovement(1,1,0)&¤tPlayer==1)
P_cursor[1].r+=1;
break;
}
case 'l':{
if(checkMovement(1,0,-1)&¤tPlayer==1)
P_cursor[1].c-=1;
break;
}
}
}
}
bool checkMovement(int player,int r_dist,int c_dist){
int row=P_cursor[player].r+r_dist;
int col=P_cursor[player].c+c_dist;
if(row>7||row<0)
return false;
if(col>7||col<0){
return false;
}
return true;
}
bool checkShipPlacement(int player,int row, int col,int length){
for(int i=0;i<length;i++){
if(ver[player]){
if(Ships[player][row+i]&(1<<col))
return false;
}
else if(Ships[player][row]&(1<<(col+i)))
return false;
}
return true;
}
void placeShip(int player){
int length=(8+nr_ships[player])/3;
if(!checkShipPlacement(player,P_cursor[player].r,P_cursor[player].c,length))
return;
P_ships[player][nr_ships[player]]={P_cursor[player].r,P_cursor[player].c,length,ver[player],0};
nr_ships[player]++;
for (int i = 0; i < length; i++) {
if (ver[player]) {
Ships[player][P_cursor[player].r + i] |= (1 << P_cursor[player].c);
} else {
Ships[player][P_cursor[player].r] |= (1 << (P_cursor[player].c + i));
}
}
P_cursor[player].r=0;
P_cursor[player].c=0;
if(nr_ships[player]==5)
P_cursor[player].active=!P_cursor[player].active;
}
void rotateShip(int player,int length){
if ((!ver[player] && (P_cursor[player].r + length) > 8) || (ver[player] && (P_cursor[player].c + length) > 8))
return;
ver[player] = !ver[player];
}
void displayPlacePhase(){
for(int row = 0; row < 8; row++){
unsigned char displayData[2]= {Ships[0][row], Ships[1][row]};
for(int player=0;player<2;player++){
if(P_cursor[player].active){
int length=(8+nr_ships[player])/3;
if(ver[player]){
if(P_cursor[player].r<=row&&P_cursor[player].r+length-1>=row)
displayData[player]=View_Placement(Ships[player][row],(1<<P_cursor[player].c));
}
else{
if(row==P_cursor[player].r){
unsigned char temp=0x00;
for(int j=0;j<length;j++)
temp|= (1 << (P_cursor[player].c + j));
displayData[player]=View_Placement(Ships[player][row],temp);
}
else
displayData[player]=Ships[player][row];
}
}
}
sendToMaxAll(row + 1, displayData[0], displayData[1]);
}
}
void displayBattlePhase() {
for(int row = 0; row < 8; row++){
unsigned char displayData[2];
for(int player=0;player<2;player++){
if(P_cursor[player].viewSelf){
displayData[player]=View_Self(LEDS[player][row],Ships[player][row]);
if(P_cursor[1-player].r==row&&P_cursor[1-player].active){
displayData[player] ^= (f2 << P_cursor[1-player].c);
}
}
else{
displayData[player]=View_Enemy(LEDS[1-player][row],Ships[1-player][row]);
if(P_cursor[player].r==row&&P_cursor[player].active){
displayData[player] ^= (f2 << P_cursor[player].c);
}
}
}
sendToMaxAll(row + 1, displayData[0], displayData[1]);
}
}
void updatePlayerTurn() {
P_cursor[0].viewSelf = (currentPlayer == 0);
P_cursor[0].active = (currentPlayer == 1);
P_cursor[1].viewSelf = (currentPlayer == 1);
P_cursor[1].active = (currentPlayer == 0);
currentPlayer=!currentPlayer;
}
void checkHit(int player){
int enemy = 1 - player;
if(LEDS[enemy][P_cursor[player].r]&(1<<P_cursor[player].c)){
return;
}
bool hitt = false;
for (int i = 0; i < 5; i++) {
Ship &ship = P_ships[enemy][i];
if (ship.hits<ship.length) {
for (int j = 0; j < ship.length; j++) {
int shipRow = ship.row + (ship.vertical ? j : 0);
int shipCol = ship.col + (ship.vertical ? 0 : j);
if (shipRow == P_cursor[player].r && shipCol == P_cursor[player].c) {
hitt=true;
ship.hits++;
if (ship.hits == ship.length) {
Serial.println("Sunk!");
nr_ships[enemy]--;
}
else{
Serial.println("Hit!");
}
break;
}
}
if (hitt)
break;
}
}
if(!hitt)
updatePlayerTurn();
if(nr_ships[enemy]==0)
winner=player;
LEDS[enemy][P_cursor[player].r] |= (1 << P_cursor[player].c);
}