I've found this code for tic tac toe game using buttons : https://create.arduino.cc/projecthub/giobbino/easiest-tictactoe-with-and-without-an-oled-display-f06231?ref=platform&ref_id=424_trending_part__&offset=3
The code works fine in online simulator wokwi.com
Then I tried making schema using proteus and it appears that my display ssd1306 isn't working at all:
On website I took code from there is also pieces of code for "not working display" but they are constantly crashing as well.
The code for Display Source Code (.cpp)
/*********************************************************************
This is a library for our Monochrome OLEDs based on SSD1306 drivers
Pick one up today in the adafruit shop!
------> http://www.adafruit.com/category/63_98
These displays use SPI to communicate, 4 or 5 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above, and the splash screen below must be included in any redistribution
*********************************************************************/
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#ifdef __AVR__
#include <avr/pgmspace.h>
#elif defined(ESP8266) || defined(ESP32)
#include <pgmspace.h>
#else
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#if !defined(__ARM_ARCH) && !defined(ENERGIA) && !defined(ESP8266) && !defined(ESP32) && !defined(__arc__)
#include <util/delay.h>
#endif
#include <stdlib.h>
#include <Wire.h>
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
// the memory buffer for the LCD
static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80,
0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF,
#if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16)
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
0x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8,
0xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80,
0x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01,
0x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF,
0xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00,
0x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF,
0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF,
0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F,
0x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03,
0x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01,
0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00,
0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03,
0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#if (SSD1306_LCDHEIGHT == 64)
0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x1F, 0x0F,
0x87, 0xC7, 0xF7, 0xFF, 0xFF, 0x1F, 0x1F, 0x3D, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0x7C, 0x7D, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x00, 0x30, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F,
0x0F, 0x07, 0x1F, 0x7F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00,
0x00, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xF8, 0x1C, 0x0E,
0x06, 0x06, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFC,
0xFE, 0xFC, 0x00, 0x18, 0x3C, 0x7E, 0x66, 0xE6, 0xCE, 0x84, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x06,
0x06, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xC0, 0xF8,
0xFC, 0x4E, 0x46, 0x46, 0x46, 0x4E, 0x7C, 0x78, 0x40, 0x18, 0x3C, 0x76, 0xE6, 0xCE, 0xCC, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x0C,
0x18, 0x18, 0x0C, 0x06, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0E, 0x0C, 0x18, 0x0C, 0x0F,
0x07, 0x01, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00,
0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x07,
0x07, 0x0C, 0x0C, 0x18, 0x1C, 0x0C, 0x06, 0x06, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#endif
#endif
};
#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; }
// the most basic function, set a single pixel
void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) {
if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
return;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 1:
ssd1306_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
break;
case 3:
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
break;
}
// x is which column
switch (color)
{
case WHITE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] |= (1 << (y&7)); break;
case BLACK: buffer[x+ (y/8)*SSD1306_LCDWIDTH] &= ~(1 << (y&7)); break;
case INVERSE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] ^= (1 << (y&7)); break;
}
}
Adafruit_SSD1306::Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) {
cs = CS;
rst = RST;
dc = DC;
sclk = SCLK;
sid = SID;
hwSPI = false;
}
// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset
Adafruit_SSD1306::Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) {
dc = DC;
rst = RST;
cs = CS;
hwSPI = true;
}
// initializer for I2C - we only indicate the reset pin!
Adafruit_SSD1306::Adafruit_SSD1306(int8_t reset) :
Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) {
sclk = dc = cs = sid = -1;
rst = reset;
}
void Adafruit_SSD1306::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) {
_vccstate = vccstate;
_i2caddr = i2caddr;
// set pin directions
if (sid != -1){
pinMode(dc, OUTPUT);
pinMode(cs, OUTPUT);
#ifdef HAVE_PORTREG
csport = portOutputRegister(digitalPinToPort(cs));
cspinmask = digitalPinToBitMask(cs);
dcport = portOutputRegister(digitalPinToPort(dc));
dcpinmask = digitalPinToBitMask(dc);
#endif
if (!hwSPI){
// set pins for software-SPI
pinMode(sid, OUTPUT);
pinMode(sclk, OUTPUT);
#ifdef HAVE_PORTREG
clkport = portOutputRegister(digitalPinToPort(sclk));
clkpinmask = digitalPinToBitMask(sclk);
mosiport = portOutputRegister(digitalPinToPort(sid));
mosipinmask = digitalPinToBitMask(sid);
#endif
}
if (hwSPI){
SPI.begin();
#ifdef SPI_HAS_TRANSACTION
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
#else
SPI.setClockDivider (4);
#endif
}
}
else
{
// I2C Init
Wire.begin();
#ifdef __SAM3X8E__
// Force 400 KHz I2C, rawr! (Uses pins 20, 21 for SDA, SCL)
TWI1->TWI_CWGR = 0;
TWI1->TWI_CWGR = ((VARIANT_MCK / (2 * 400000)) - 4) * 0x101;
#endif
}
if ((reset) && (rst >= 0)) {
// Setup reset pin direction (used by both SPI and I2C)
pinMode(rst, OUTPUT);
digitalWrite(rst, HIGH);
// VDD (3.3V) goes high at start, lets just chill for a ms
delay(1);
// bring reset low
digitalWrite(rst, LOW);
// wait 10ms
delay(10);
// bring out of reset
digitalWrite(rst, HIGH);
// turn on VCC (9V?)
}
// Init sequence
ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE
ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
ssd1306_command(0x80); // the suggested ratio 0x80
ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8
ssd1306_command(SSD1306_LCDHEIGHT - 1);
ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3
ssd1306_command(0x0); // no offset
ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0
ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D
if (vccstate == SSD1306_EXTERNALVCC)
{ ssd1306_command(0x10); }
else
{ ssd1306_command(0x14); }
ssd1306_command(SSD1306_MEMORYMODE); // 0x20
ssd1306_command(0x00); // 0x0 act like ks0108
ssd1306_command(SSD1306_SEGREMAP | 0x1);
ssd1306_command(SSD1306_COMSCANDEC);
#if defined SSD1306_128_32
ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
ssd1306_command(0x02);
ssd1306_command(SSD1306_SETCONTRAST); // 0x81
ssd1306_command(0x8F);
#elif defined SSD1306_128_64
ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
ssd1306_command(0x12);
ssd1306_command(SSD1306_SETCONTRAST); // 0x81
if (vccstate == SSD1306_EXTERNALVCC)
{ ssd1306_command(0x9F); }
else
{ ssd1306_command(0xCF); }
#elif defined SSD1306_96_16
ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
ssd1306_command(0x2); //ada x12
ssd1306_command(SSD1306_SETCONTRAST); // 0x81
if (vccstate == SSD1306_EXTERNALVCC)
{ ssd1306_command(0x10); }
else
{ ssd1306_command(0xAF); }
#endif
ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
if (vccstate == SSD1306_EXTERNALVCC)
{ ssd1306_command(0x22); }
else
{ ssd1306_command(0xF1); }
ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
ssd1306_command(0x40);
ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4
ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6
ssd1306_command(SSD1306_DEACTIVATE_SCROLL);
ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel
}
void Adafruit_SSD1306::invertDisplay(uint8_t i) {
if (i) {
ssd1306_command(SSD1306_INVERTDISPLAY);
} else {
ssd1306_command(SSD1306_NORMALDISPLAY);
}
}
void Adafruit_SSD1306::ssd1306_command(uint8_t c) {
if (sid != -1)
{
// SPI
#ifdef HAVE_PORTREG
*csport |= cspinmask;
*dcport &= ~dcpinmask;
*csport &= ~cspinmask;
#else
digitalWrite(cs, HIGH);
digitalWrite(dc, LOW);
digitalWrite(cs, LOW);
#endif
fastSPIwrite(c);
#ifdef HAVE_PORTREG
*csport |= cspinmask;
#else
digitalWrite(cs, HIGH);
#endif
}
else
{
// I2C
uint8_t control = 0x00; // Co = 0, D/C = 0
Wire.beginTransmission(_i2caddr);
Wire.write(control);
Wire.write(c);
Wire.endTransmission();
}
}
// startscrollright
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop){
ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL);
ssd1306_command(0X00);
ssd1306_command(start);
ssd1306_command(0X00);
ssd1306_command(stop);
ssd1306_command(0X00);
ssd1306_command(0XFF);
ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}
// startscrollleft
// Activate a right handed scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop){
ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL);
ssd1306_command(0X00);
ssd1306_command(start);
ssd1306_command(0X00);
ssd1306_command(stop);
ssd1306_command(0X00);
ssd1306_command(0XFF);
ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}
// startscrolldiagright
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){
ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA);
ssd1306_command(0X00);
ssd1306_command(SSD1306_LCDHEIGHT);
ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
ssd1306_command(0X00);
ssd1306_command(start);
ssd1306_command(0X00);
ssd1306_command(stop);
ssd1306_command(0X01);
ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}
// startscrolldiagleft
// Activate a diagonal scroll for rows start through stop
// Hint, the display is 16 rows tall. To scroll the whole display, run:
// display.scrollright(0x00, 0x0F)
void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){
ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA);
ssd1306_command(0X00);
ssd1306_command(SSD1306_LCDHEIGHT);
ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
ssd1306_command(0X00);
ssd1306_command(start);
ssd1306_command(0X00);
ssd1306_command(stop);
ssd1306_command(0X01);
ssd1306_command(SSD1306_ACTIVATE_SCROLL);
}
void Adafruit_SSD1306::stopscroll(void){
ssd1306_command(SSD1306_DEACTIVATE_SCROLL);
}
// Dim the display
// dim = true: display is dimmed
// dim = false: display is normal
void Adafruit_SSD1306::dim(boolean dim) {
uint8_t contrast;
if (dim) {
contrast = 1; //0; // Dimmed display - If = zero, the display is off.
} else {
if (_vccstate == SSD1306_EXTERNALVCC) {
contrast = 0x9F;
} else {
contrast = 0xCF;
}
}
// the range of contrast to too small to be really useful
// it is useful to dim the display
ssd1306_command(SSD1306_SETCONTRAST);
ssd1306_command(contrast);
}
void Adafruit_SSD1306::display(void) {
ssd1306_command(SSD1306_COLUMNADDR);
ssd1306_command(0); // Column start address (0 = reset)
ssd1306_command(SSD1306_LCDWIDTH-1); // Column end address (127 = reset)
ssd1306_command(SSD1306_PAGEADDR);
ssd1306_command(0); // Page start address (0 = reset)
#if SSD1306_LCDHEIGHT == 64
ssd1306_command(7); // Page end address
#endif
#if SSD1306_LCDHEIGHT == 32
ssd1306_command(3); // Page end address
#endif
#if SSD1306_LCDHEIGHT == 16
ssd1306_command(1); // Page end address
#endif
if (sid != -1)
{
// SPI
#ifdef HAVE_PORTREG
*csport |= cspinmask;
*dcport |= dcpinmask;
*csport &= ~cspinmask;
#else
digitalWrite(cs, HIGH);
digitalWrite(dc, HIGH);
digitalWrite(cs, LOW);
#endif
for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
fastSPIwrite(buffer[i]);
}
#ifdef HAVE_PORTREG
*csport |= cspinmask;
#else
digitalWrite(cs, HIGH);
#endif
}
else
{
// save I2C bitrate
#ifdef TWBR
uint8_t twbrbackup = TWBR;
TWBR = 12; // upgrade to 400KHz!
#endif
//Serial.println(TWBR, DEC);
//Serial.println(TWSR & 0x3, DEC);
// I2C
for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) {
// send a bunch of data in one xmission
Wire.beginTransmission(_i2caddr);
WIRE_WRITE(0x40);
for (uint8_t x=0; x<16; x++) {
WIRE_WRITE(buffer[i]);
i++;
}
i--;
Wire.endTransmission();
}
#ifdef TWBR
TWBR = twbrbackup;
#endif
}
}
// clear everything
void Adafruit_SSD1306::clearDisplay(void) {
memset(buffer, 0, (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8));
}
inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) {
if(hwSPI) {
(void)SPI.transfer(d);
} else {
for(uint8_t bit = 0x80; bit; bit >>= 1) {
#ifdef HAVE_PORTREG
*clkport &= ~clkpinmask;
if(d & bit) *mosiport |= mosipinmask;
else *mosiport &= ~mosipinmask;
*clkport |= clkpinmask;
#else
digitalWrite(sclk, LOW);
if(d & bit) digitalWrite(sid, HIGH);
else digitalWrite(sid, LOW);
digitalWrite(sclk, HIGH);
#endif
}
}
}
void Adafruit_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
boolean bSwap = false;
switch(rotation) {
case 0:
// 0 degree rotation, do nothing
break;
case 1:
// 90 degree rotation, swap x & y for rotation, then invert x
bSwap = true;
ssd1306_swap(x, y);
x = WIDTH - x - 1;
break;
case 2:
// 180 degree rotation, invert x and y - then shift y around for height.
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
x -= (w-1);
break;
case 3:
// 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h)
bSwap = true;
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
y -= (w-1);
break;
}
if(bSwap) {
drawFastVLineInternal(x, y, w, color);
} else {
drawFastHLineInternal(x, y, w, color);
}
}
void Adafruit_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) {
// Do bounds/limit checks
if(y < 0 || y >= HEIGHT) { return; }
// make sure we don't try to draw below 0
if(x < 0) {
w += x;
x = 0;
}
// make sure we don't go off the edge of the display
if( (x + w) > WIDTH) {
w = (WIDTH - x);
}
// if our width is now negative, punt
if(w <= 0) { return; }
// set up the pointer for movement through the buffer
register uint8_t *pBuf = buffer;
// adjust the buffer pointer for the current row
pBuf += ((y/8) * SSD1306_LCDWIDTH);
// and offset x columns in
pBuf += x;
register uint8_t mask = 1 << (y&7);
switch (color)
{
case WHITE: while(w--) { *pBuf++ |= mask; }; break;
case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break;
case INVERSE: while(w--) { *pBuf++ ^= mask; }; break;
}
}
void Adafruit_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
bool bSwap = false;
switch(rotation) {
case 0:
break;
case 1:
// 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
bSwap = true;
ssd1306_swap(x, y);
x = WIDTH - x - 1;
x -= (h-1);
break;
case 2:
// 180 degree rotation, invert x and y - then shift y around for height.
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
y -= (h-1);
break;
case 3:
// 270 degree rotation, swap x & y for rotation, then invert y
bSwap = true;
ssd1306_swap(x, y);
y = HEIGHT - y - 1;
break;
}
if(bSwap) {
drawFastHLineInternal(x, y, h, color);
} else {
drawFastVLineInternal(x, y, h, color);
}
}
void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) {
// do nothing if we're off the left or right side of the screen
if(x < 0 || x >= WIDTH) { return; }
// make sure we don't try to draw below 0
if(__y < 0) {
// __y is negative, this will subtract enough from __h to account for __y being 0
__h += __y;
__y = 0;
}
// make sure we don't go past the height of the display
if( (__y + __h) > HEIGHT) {
__h = (HEIGHT - __y);
}
// if our height is now negative, punt
if(__h <= 0) {
return;
}
// this display doesn't need ints for coordinates, use local byte registers for faster juggling
register uint8_t y = __y;
register uint8_t h = __h;
// set up the pointer for fast movement through the buffer
register uint8_t *pBuf = buffer;
// adjust the buffer pointer for the current row
pBuf += ((y/8) * SSD1306_LCDWIDTH);
// and offset x columns in
pBuf += x;
// do the first partial byte, if necessary - this requires some masking
register uint8_t mod = (y&7);
if(mod) {
// mask off the high n bits we want to set
mod = 8-mod;
// note - lookup table results in a nearly 10% performance improvement in fill* functions
// register uint8_t mask = ~(0xFF >> (mod));
static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
register uint8_t mask = premask[mod];
// adjust the mask if we're not going to reach the end of this byte
if( h < mod) {
mask &= (0XFF >> (mod-h));
}
switch (color)
{
case WHITE: *pBuf |= mask; break;
case BLACK: *pBuf &= ~mask; break;
case INVERSE: *pBuf ^= mask; break;
}
// fast exit if we're done here!
if(h<mod) { return; }
h -= mod;
pBuf += SSD1306_LCDWIDTH;
}
// write solid bytes while we can - effectively doing 8 rows at a time
if(h >= 8) {
if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop
do {
*pBuf=~(*pBuf);
// adjust the buffer forward 8 rows worth of data
pBuf += SSD1306_LCDWIDTH;
// adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
h -= 8;
} while(h >= 8);
}
else {
// store a local value to work with
register uint8_t val = (color == WHITE) ? 255 : 0;
do {
// write our value in
*pBuf = val;
// adjust the buffer forward 8 rows worth of data
pBuf += SSD1306_LCDWIDTH;
// adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
h -= 8;
} while(h >= 8);
}
}
// now do the final partial byte, if necessary
if(h) {
mod = h & 7;
// this time we want to mask the low bits of the byte, vs the high bits we did above
// register uint8_t mask = (1 << mod) - 1;
// note - lookup table results in a nearly 10% performance improvement in fill* functions
static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
register uint8_t mask = postmask[mod];
switch (color)
{
case WHITE: *pBuf |= mask; break;
case BLACK: *pBuf &= ~mask; break;
case INVERSE: *pBuf ^= mask; break;
}
}
}
The code for Display Source Code (.h)
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#ifndef _Adafruit_SSD1306_H_
#define _Adafruit_SSD1306_H_
#if ARDUINO >= 100
#include "Arduino.h"
#define WIRE_WRITE Wire.write
#else
#include "WProgram.h"
#define WIRE_WRITE Wire.send
#endif
#if defined(__SAM3X8E__)
typedef volatile RwReg PortReg;
typedef uint32_t PortMask;
#define HAVE_PORTREG
#elif defined(ARDUINO_ARCH_SAMD)
// not supported
#elif defined(ESP8266) || defined(ESP32) || defined(ARDUINO_STM32_FEATHER) || defined(__arc__)
typedef volatile uint32_t PortReg;
typedef uint32_t PortMask;
#elif defined(__AVR__)
typedef volatile uint8_t PortReg;
typedef uint8_t PortMask;
#define HAVE_PORTREG
#else
// chances are its 32 bit so assume that
typedef volatile uint32_t PortReg;
typedef uint32_t PortMask;
#endif
#include <SPI.h>
#include <Adafruit_GFX.h>
#define BLACK 0
#define WHITE 1
#define INVERSE 2
/*=========================================================================
SSD1306 Displays
-----------------------------------------------------------------------
The driver is used in multiple displays (128x64, 128x32, etc.).
Select the appropriate display below to create an appropriately
sized framebuffer, etc.
SSD1306_128_64 128x64 pixel display
SSD1306_128_32 128x32 pixel display
SSD1306_96_16
-----------------------------------------------------------------------*/
//------------------------------------------------------------------------------------|
//modified by Giovanni Verrua. Change it if the display doesn't work
#define SSD1306_I2C_ADDRESS 0x3D // 011110+SA0+RW - 0x3C or 0x3D
// Address for 128x32 is 0x3C
// Address for 128x64 is 0x3D (default) or 0x3C (if SA0 is grounded)
#define SSD1306_128_64
// #define SSD1306_128_32
// #define SSD1306_96_16
//------------------------------------------------------------------------------------|
/*=========================================================================*/
#if defined SSD1306_128_64 && defined SSD1306_128_32
#error "Only one SSD1306 display can be specified at once in SSD1306.h"
#endif
#if !defined SSD1306_128_64 && !defined SSD1306_128_32 && !defined SSD1306_96_16
#error "At least one SSD1306 display must be specified in SSD1306.h"
#endif
#if defined SSD1306_128_64
#define SSD1306_LCDWIDTH 128
#define SSD1306_LCDHEIGHT 64
#endif
#if defined SSD1306_128_32
#define SSD1306_LCDWIDTH 128
#define SSD1306_LCDHEIGHT 32
#endif
#if defined SSD1306_96_16
#define SSD1306_LCDWIDTH 96
#define SSD1306_LCDHEIGHT 16
#endif
#define SSD1306_SETCONTRAST 0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR 0x22
#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2
// Scrolling #defines
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
class Adafruit_SSD1306 : public Adafruit_GFX {
public:
Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS);
Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS);
Adafruit_SSD1306(int8_t RST = -1);
void begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = SSD1306_I2C_ADDRESS, bool reset=true);
void ssd1306_command(uint8_t c);
void clearDisplay(void);
void invertDisplay(uint8_t i);
void display();
void startscrollright(uint8_t start, uint8_t stop);
void startscrollleft(uint8_t start, uint8_t stop);
void startscrolldiagright(uint8_t start, uint8_t stop);
void startscrolldiagleft(uint8_t start, uint8_t stop);
void stopscroll(void);
void dim(boolean dim);
void drawPixel(int16_t x, int16_t y, uint16_t color);
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
private:
int8_t _i2caddr, _vccstate, sid, sclk, dc, rst, cs;
void fastSPIwrite(uint8_t c);
boolean hwSPI;
#ifdef HAVE_PORTREG
PortReg *mosiport, *clkport, *csport, *dcport;
PortMask mosipinmask, clkpinmask, cspinmask, dcpinmask;
#endif
inline void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color) __attribute__((always_inline));
inline void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) __attribute__((always_inline));
};
#endif /* _Adafruit_SSD1306_H_ */
main file looks like this:
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
//including the needed libraries for the OLED display
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define BUTTON_MOVE 2
#define BUTTON_OK 3
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);
int gameStatus = 0;
int whosplaying = 0; //0 = Arduino, 1 = Human
int winner = -1; //-1 = Playing, 0 = Draw, 1 = Human, 2 = CPU
int board[]={0,0,0,
0,0,0,
0,0,0}; //0 = blank, 1 = human (circle), 2 = computer (cross)
//--------------------------------------------------------------------------------------------------------
void playhuman() {
int humanMove = 0;
bool stayInLoop = true;
bool showDot = false;
long timerPos = millis()-1000;
while (stayInLoop) { //stay in loop until the player makes his/her choice hitting the OK button.
//If the current "?" position isn't avaliable (the cell value is 1 or 2), this loop will
//move the "?" to the next free cell.
//NOTE: there must be at least one empty cell (or it will never exit from this loop [deadlock]).
//This is granted, because if all the cells are used, there's a winner or it's a draft.
//(the calling function [loop function] check it before to continue).
while (board[humanMove] != 0) { //looking for an empty cell.
humanMove ++;
if (humanMove >8) humanMove = 0;
}
//--------------------------------------------------\-
//this makes the flashing "?" possible. Every 200 milliseconds the IF condition becomes true and it will toogle the
//showDot variable between True and False (and reset the timerPos value at the current millis() value.
if (timerPos + 200 < millis()) {
timerPos = millis();
showDot = !showDot;
playhuman_showpos( humanMove, showDot); //calling the function that will draw (or delete) the "?"
}
//--------------------------------------------------/-
if (digitalRead(BUTTON_MOVE)==LOW) { //the player hit the MOVE button.
playhuman_showpos( humanMove, false); //delete the marker
humanMove ++; //move the "?" to the next cell.
while (digitalRead(BUTTON_MOVE)==LOW); //debounce
bool showDot = false; //this two lines make sure the "?" is displayed
long timerPos =-1000; //at the first round.
}
if (digitalRead(BUTTON_OK )==LOW) stayInLoop = false; //the player hit the OK button and made his/her choice.
delay(100); //required for a correct display.
}
board[humanMove] = 1; //let's assign the chosen cell to the player.
}
//------------------------------------------------------------------
void playhuman_showpos(int humanMove, bool showDot) { //this function draw a flashing "?" (white = draw, black = delete)
display.setTextSize(2);
if (humanMove == 0) display.setCursor( 5, 5);
else if (humanMove == 1) display.setCursor(25, 5);
else if (humanMove == 2) display.setCursor(45, 5);
else if (humanMove == 3) display.setCursor( 5,25);
else if (humanMove == 4) display.setCursor(25,25);
else if (humanMove == 5) display.setCursor(45,25);
else if (humanMove == 6) display.setCursor( 5,45);
else if (humanMove == 7) display.setCursor(25,45);
else if (humanMove == 8) display.setCursor(45,45);
//if (showDot) {display.setTextColor(WHITE);display.print("?");} else {display.setTextColor(BLACK);display.print("?");}
if (showDot) display.setTextColor(WHITE); else display.setTextColor(BLACK);
display.print("?");
display.display();
}
//--------------------------------------------------------------------------------------------------------
void playcpu() {
//NOTE: The player has almost no chance to win, since the cpu will check every possible move.
// Actually the only way to beat the cpu is to have two winning move at the same time.
//The CPU has no real strategy, actually; it just prevents the player to win and put an "X" if it has
//a possible winning move. If no winning move are possible, it just put an "X" into a random place.
//It could seems a stupid AI, however you will see the CPU will play rather well and it will be
//hard to beat it.
int cpumove = checkboard(2); //2 = cpu let's check if there's a cpu's winner move
if (cpumove >=0) {
board[cpumove] = 2; //cpu's winner move
}
else {
cpumove = checkboard(1); //1=player check if the player has a chance to win (2 circles and an empty cell in a row)
if (cpumove >=0) {
board[cpumove] = 2; //this move will break the player's winner move
}
//there's no possible winner move neither for the cpu, nor for the human;: the CPU will put an "X" in a random cell
while (cpumove < 0) { //looking for a random, empty cell.
int randomMove = random(10);
if (randomMove >=0 && randomMove <=8 && board[randomMove] == 0) {
cpumove = randomMove;
}
}
board[cpumove] = 2; //let's assign the empty cell to the CPU
}
}
//--------------------------------------------------------------------------------------------------------
int checkboard(int x){ //x = 1 -> player, x = 2 -> cpu
//this function checks if the next move can be the winning move and return the cell that will
//win the game. It's used by the CPU to decide if it can win or if the player is going to win
//(and placing an "X" to prevent this chance).
//if no move wins the game, it returns -1
//the board[] index is 0 1 2
// 3 4 5
// 6 7 8
if (board[0]==0 && board[1]==x && board[2]==x) return 0; // 0 1 1
// . . .
// . . .
else if (board[0]==x && board[1]==0 && board[2]==x) return 1; // 1 0 1
// . . .
// . . .
else if (board[0]==x && board[1]==x && board[2]==0) return 2; // 1 1 0
// . . .
// . . .
//-------------------------------------------------
else if (board[3]==0 && board[4]==x && board[5]==x) return 3; // . . .
// 0 1 1
// . . .
else if (board[3]==x && board[4]==0 && board[5]==x) return 4; // . . .
// 1 0 1
// . . .
else if (board[3]==x && board[4]==x && board[5]==0) return 5; // . . .
// 1 1 0
// . . .
//-------------------------------------------------
else if (board[6]==0 && board[7]==x && board[8]==x) return 6; // . . .
// . . .
// 0 1 1
else if (board[6]==x && board[7]==0 && board[8]==x) return 7; // . . .
// . . .
// 1 0 1
else if (board[6]==x && board[7]==x && board[8]==0) return 8; // . . .
// . . .
// 1 1 0
//-------------------------------------------------
else if (board[0]==0 && board[3]==x && board[6]==x) return 0; // 0 . .
// 1 . .
// 1 . .
else if (board[0]==x && board[3]==0 && board[6]==x) return 3; // 1 . .
// 0 . .
// 1 . .
else if (board[0]==x && board[3]==x && board[6]==0) return 6; // 1 . .
// 1 . .
// 0 . .
//-------------------------------------------------
else if (board[1]==0 && board[4]==x && board[7]==x) return 1; // . 0 .
// . 1 .
// . 1 .
else if (board[1]==x && board[4]==0 && board[7]==x) return 4; // . 1 .
// . 0 .
// . 1 .
else if (board[1]==x && board[4]==x && board[7]==0) return 7; // . 1 .
// . 1 .
// . 0 .
//-------------------------------------------------
else if (board[2]==0 && board[5]==x && board[8]==x) return 2; // . . 0
// . . 1
// . . 1
else if (board[2]==x && board[5]==0 && board[8]==x) return 5; // . . 1
// . . 0
// . . 1
else if (board[2]==x && board[5]==x && board[8]==0) return 8; // . . 1
// . . 1
// . . 0
//-------------------------------------------------
else if (board[0]==0 && board[4]==x && board[8]==x) return 0; // 0 . .
// . 1 .
// . . 1
else if (board[0]==x && board[4]==0 && board[8]==x) return 4; // 1 . .
// . 0 .
// . . 1
else if (board[0]==x && board[4]==x && board[8]==0) return 8; // 1 . .
// . 1 .
// . . 0
//-------------------------------------------------
else if (board[2]==0 && board[4]==x && board[6]==x) return 2; // . . 0
// . 1 .
// 1 . .
else if (board[2]==x && board[4]==0 && board[6]==x) return 4; // . . 1
// . 0 .
// 1 . .
else if (board[2]==x && board[4]==x && board[6]==0) return 6; // . . 1
// . 1 .
// 0 . .
else return -1;
}
//--------------------------------------------------------------------------------------------
void checkWinner() { //check the board to see if there is a winner
winner = 3; //3=draft, 1= winner->player, 2=winner->cpu
// circles win?
if (board[0]==1 && board[1]==1 && board[2]==1) winner=1;
else if (board[3]==1 && board[4]==1 && board[5]==1) winner=1;
else if (board[6]==1 && board[7]==1 && board[8]==1) winner=1;
else if (board[0]==1 && board[3]==1 && board[6]==1) winner=1;
else if (board[1]==1 && board[4]==1 && board[7]==1) winner=1;
else if (board[2]==1 && board[5]==1 && board[8]==1) winner=1;
else if (board[0]==1 && board[4]==1 && board[8]==1) winner=1;
else if (board[2]==1 && board[4]==1 && board[6]==1) winner=1;
// crosses win?
else if (board[0]==2 && board[1]==2 && board[2]==2) winner=2;
else if (board[3]==2 && board[4]==2 && board[5]==2) winner=2;
else if (board[6]==2 && board[7]==2 && board[8]==2) winner=2;
else if (board[0]==2 && board[3]==2 && board[6]==2) winner=2;
else if (board[1]==2 && board[4]==2 && board[7]==2) winner=2;
else if (board[2]==2 && board[5]==2 && board[8]==2) winner=2;
else if (board[0]==2 && board[4]==2 && board[8]==2) winner=2;
else if (board[2]==2 && board[4]==2 && board[6]==2) winner=2;
if (winner == 3) {
for(int i=0;i<9;i++) if (board[i]==0) winner=0; //there are some empty cells yet.
}
}
//--------------------------------------------------------------------------------------------------------------
void resetGame() {
for(int i=0;i<9;i++) board[i]=0; //Resetting the board. 0 = empty cell, 1 = player circle, 2 = CPU cross
winner = 0;
gameStatus = 0;
}
//--------------------------------------------------------------------------------------------------------------
void boardDrawing() {
display.clearDisplay();
display.setTextColor(WHITE);
display.drawFastHLine(0, 21, 64, WHITE); //horizontal lines
display.drawFastHLine(0, 42, 64, WHITE);
display.drawFastVLine(21, 0, 64, WHITE); //vertical lines
display.drawFastVLine(42, 0, 64, WHITE);
//drawing the content of the nine cells: " ", "o", "x"
display.setTextSize(2);
display.setCursor( 5, 5); display.print(charBoard(0)); display.setCursor(25, 5); display.print(charBoard(1)); display.setCursor(45, 5); display.print(charBoard(2));
display.setCursor( 5,25); display.print(charBoard(3)); display.setCursor(25,25); display.print(charBoard(4)); display.setCursor(45,25); display.print(charBoard(5));
display.setCursor( 5,45); display.print(charBoard(6)); display.setCursor(25,45); display.print(charBoard(7)); display.setCursor(45,45); display.print(charBoard(8));
display.display();
delay(200); //DON'T REMOVE!!!! needed for correct refresh and further flashing "?" when it's the player turn!!!
}
//--------------------------------------------------------------------------------------------------------------
String charBoard(int x) {
if (board[x] == 0) return " ";
if (board[x] == 1) return "o";
if (board[x] == 2) return "x";
return "?"; //error trap; but it's impossible it can return an "?" because the board[] array is all initialized = 0
}
//--------------------------------------------------------------------------------------------------------------
void setup() { //function executed once, at every boot or reset.
randomSeed(analogRead(0)); //resetting the random function behavior.
//using the internal pullup resistor, so the button will be LOW until a button is pressed.
pinMode(BUTTON_MOVE,INPUT_PULLUP); //Declaring the pin #2 as input (connected to the button move)
pinMode(BUTTON_OK ,INPUT_PULLUP); //Declaring the pin #3 as input (connected to the button ok)
//display.begin(); //if the display doesn't work with the beHIGH instruction,
display.begin(SSD1306_SWITCHCAPVCC, 0x3D); //try one of these two ones.
//display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
delay(500); //needed for display correct initializing
display.clearDisplay(); //clearing the display
display.setTextColor(WHITE); //setting the display color
display.display(); //executing the above instructions. The SSD1306 dislay will not execute any command until to use the ::display() command.
whosplaying = 2; //deciding who's the first player. Set = 2 to force it entering in the folHIGHing while loop.
while ( whosplaying <0 || whosplaying > 1) whosplaying = random(2); //it will stay in the loop until whosplaying isn't = 0 or = 1. Probably there's no
//need for a loop, since random(2) should return 0 or 1, but I'm an old programmer,
//I've seen many strange things in my programmer life and I like to be sure ;-)
}
//--------------------------------------------------------------------------------------------------------------
void loop() { //main loop. Endlessly executed by Arduino.
if (gameStatus == 0){ //this is where I always put a menu in the Arduino games I wrote. We don't need a menu here, so it's just a reset step.
resetGame();
boardDrawing(); //drawing an empty board
gameStatus = 1; //starting the game (see beHIGH)
winner = 0; //no winner for now (winner = 1: player, winner = 2: cpu).
}
//---------------------------------------------
if (gameStatus == 1){ //starting the game
while (winner == 0) { //game main loop: loop until no one wins the match.
display.setTextSize(2);
if (whosplaying == 0) { //whosplaying = 0: cpu turn
display.setCursor( 72,25); display.print("CPU");
display.display();
delay(1000);
playcpu(); //in this function the CPU play its move.
whosplaying =1; //changing the turn.
}
else {
display.setCursor( 72,25); display.print("You");
display.display();
playhuman(); //in this function the player makes his/her move.
whosplaying =0; //changing the turn.
}
boardDrawing(); //refreshing the board with all the moves already done.
delay(500);
checkWinner(); //this will check if there's a winner and assign the winner variable
if (winner > 0) {
if (winner == 3) {
display.setTextSize(2); display.setCursor( 68, 25);
display.print("Draft");
}
else {
//showing who's the winner
display.setTextSize(2); display.setCursor( 72, 25);
if (winner == 1) { Serial.println(F("You")); display.print("You"); display.setCursor( 72, 45); display.print("win"); }
else { Serial.println(F("CPU")); display.print("CPU"); display.setCursor( 72, 45); display.print("wins");}
}
display.display();
delay(1000);
//debounce loop. It will not proceed until both buttons are unpressed.
while (digitalRead(BUTTON_MOVE)==HIGH && digitalRead(BUTTON_OK )==HIGH);
}
display.display();
}
//swap the first move for the next match between CPU and human (one per match)
if (whosplaying == 0) whosplaying =1; else whosplaying =0;
gameStatus = 0; //entering the reset step
delay(1000); //just wait a second
}
}