# include <LedControl.h>
# include <Keypad.h>
# include <LiquidCrystal_I2C.h>
# include <Adafruit_NeoPixel.h>
# define I2C_ADDR 0x27
# define LCD_COLUMNS 16
# define LCD_LINES 2
// using an I2C device
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
//keypad init
const byte ROWS = 3;
const byte COLS = 3;
const byte keyValues[ROWS][COLS] = {
{11, 12, 13},
{21, 22, 23},
{31, 32, 33},
};
const byte colPins[COLS] = { 11, 12, 13 };
const byte rowPins[ROWS] = { 8, 9, 10 };
Keypad keypad = Keypad(makeKeymap(keyValues), rowPins, colPins, ROWS, COLS);
// alternate to 8X8 LED matrix
# define PIN_LED 5
# define NUM_LEDS 64
Adafruit_NeoPixel m8X8 = Adafruit_NeoPixel(NUM_LEDS, PIN_LED, NEO_GRB + NEO_KHZ800);
//game init
int turn;
bool gameOver;
byte nMoves;
bool prompted;
char gameTable[3][3] = {
{' ', ' ', ' '},
{' ', ' ', ' '},
{' ', ' ', ' '}
};
void resetGame()
{
turn = 1;
gameOver = false;
nMoves = 0;
prompted = false;
char *t = &gameTable[0][0];
for (byte ii = 0; ii < 9; ii++) t[ii] = ' ';
byte hash[8] = {B00100100, B00100100, B11111111, B00100100, B00100100, B11111111, B00100100, B00100100};
for (int ii = 0; ii < 8; ii++)
m8X8SetRow(0, ii, hash[ii]);
}
//misc init
unsigned long time = millis();
unsigned long lastPressed = 0;
unsigned long interval = 350;
//setup loop
void setup() {
Serial.begin(115200);
Serial.println("\nhi Mom!\n");
setupLCD();
setupM8X8();
delay(333);
m8X8Clear();
}
void setupLCD() {
// Init
lcd.init();
lcd.backlight();
}
void loop() {
char winner;
if (gameOver) {
Serial.println("Game over. RESET"); for (; ; );
resetGame();
}
if (!prompted) {
lcd.setCursor(0, 0);
if (turn == 0)
lcd.print("Player X's turn!");
else
lcd.print("Player O's turn!");
prompted = true;
}
char keyCode = keypad.getKey();
char keyX = keyCode % 10;
char keyY = keyCode / 10;
//uses coordinates from keypad input and places accordingly.
if (keyCode) {
nMoves++;
if (turn == 0) {
placeMark(keyX, keyY, 'x', 0xff0000);
turn = 1;
} else if (turn == 1) {
placeMark(keyX, keyY, 'o', 0x00ff00);
turn = 0;
}
prompted = false;
}
if (keyCode) {
winner = winDetect();
if (winner == ' ') {
if (nMoves == 9) {
lcd.setCursor(0,0);
lcd.print(" DRAW ");
gameOver = true;
}
}
else {
lcd.setCursor(0, 0);
if (winner == 'x')
lcd.print("X is the winner!");
else if (winner == 'o')
lcd.print("O is the winner!");
else
Serial.println("impossible!");
gameOver = true;
}
}
}
void setupM8X8() {
m8X8.begin();
m8X8.setPixelColor(1, 0xff0000);
m8X8.setPixelColor(2, 0x00ff00);
m8X8.setPixelColor(3, 0x0000ff);
m8X8.setPixelColor(63, 0xffffff);
m8X8.show();
}
void m8X8Clear() {
m8X8.clear();
m8X8.show();
}
void m8X8SetLED(byte ignore, byte colm, byte row, byte onOff)
{
m8X8.setPixelColor(row * 8 + colm, onOff ? 0x606060 : 0x000000);
m8X8.show();
}
void m8X8SetRow(byte ignore, byte row, byte rowBits)
{
for (byte colm = 0, mask = 0x1; colm < 8; colm++, mask <<= 1)
m8X8SetLED(0, colm, row, rowBits & mask);
}
//place mark
void placeMark(int x, int y, char mark, unsigned long color) {
int xProjected = (x - 1) * 3;
int yProjected = (y - 1) * 3;
for (byte ii = 0; ii < 2; ii++) for (byte jj = 0; jj < 2; jj++)
m8X8.setPixelColor(8 * (yProjected + ii) + xProjected + jj, color);
m8X8.show();
gameTable[x - 1][y - 1] = mark;
}
//winning patterns
const byte nWaysToWin = 8;
byte win[nWaysToWin][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // rows
{0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // columns
{0, 4, 8}, {2, 4, 6} // diagonals
};
char winDetect()
{
char *state;
char winner = ' ';
for (byte ii = 0; ii < nWaysToWin; ii++) {
state = (char *) gameTable;
char test = state[win[ii][0]];
if (test == ' ') continue;
if (state[win[ii][1]] != test) continue;
if (state[win[ii][2]] != test) continue;
winner = test;
Serial.print(winner); Serial.println(" has TIC-TAC-TOE");
break;
}
return winner;
}