Tic tac toe game with OLED display doesn't work

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
         
  }
  
}

Did you also download the .CPP and .H files? Maybe first download and make the Serial Monitor version working... then build the project for the OLED version and try again.

Hi,

Rather than use simulators, how about you build it and see if it works in the real world.
Simulators are not fool proof.

Tom... :smiley: :+1: :coffee: :australia:

1 Like

Hi! I actually did try this as well, but those files are not working, they always show errors like

  1. "not declared in this scope " - shows for all variables like sc, rst etc.
  2. expected primary-expression before '= ' token - if I try to take those values and define them above class constructors

thanks for your really valuable input !

#1 and #2 say code isn't finding the right .H file (assuming you copy/pasted the sketch).

If you edited the sketch, then look for the keyword in question. If you defined it in a function (void setup()), but called it from another function (void loop()), the "scope" is lost.

There is also the possibility that a "copy" of the code copied a "CR/LF" or "CTRL-SPACE" that HTML likes to insert. In this case (quoted), maybe a key word got mashed into the "=" sign. See if you can copy and try the raw code.

I didn't copy code but downloaded it.
And those variables are in class constructor as far as I understand it. And I tried defining them with keyword #define

Hi,
Can you please post your code that you are having problems with?

To add code please click this link;

Thanks.. Tom... :smiley: :coffee: :australia:

I added code files from site to the post

Make sure the sketch can see all these include files...

#include <avr/pgmspace.h>
#include <pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#include <Wire.h>
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

Paste a copy of them in the tictactoe folder. There is a better way to do this, but for now, make these files "visible" by placing them inside tictactoe. Once it work, then we'll clean it up.

I`m not quite sure what do you mean by "Paste a copy of them in the folder".
But id adding them to my main file it writes pgmspace.h: No such file or directory

The "copy/paste" was my lazy way of putting a copy of every needed file in my sketch folder so when compiling, I would not need to instruct the IDE on the location of the files.

You can achieve the same effect by selecting SKETCH >> ADD LIBRARIES >> (find all your #includes).

pgmspace.h error is telling me that your main libraries folder is hidden from the IDE.

But then why other libraries work fine? I don't understand it, and there is no library pgmspace in "add libraries" - I can't find it by name like I`ve done with others

I see my mistake... keep this next line:

#include <avr/pgmspace.h>

remove the following line:

#include <pgmspace.h>

"avr/pgmspace.h" lives in the /avr/ directory, and was installed with the IDE.

Computers like to make things disappear A LOT. My prmspace.h is located here...

/arduino-1.8.19/hardware/tools/avr/avr/include/avr/pgmspace.h

You can look for yours with a normal file search. Usually, the IDE will notify you of old or missing libraries. Check your libraries for updatable files like this:

SKETCH >> INCLUDE LIBRARIES >> MANAGE LIBRARIES (or SHFT-CTRL-I)

"install" or "update" all the files in the notification.

Okay, I updated post with code that I`m using right now and mistakes are:

 In constructor 'Adafruit_SSD1306::Adafruit_SSD1306(int8_t, int8_t, int8_t, int8_t, int8_t)':

display_source_code___cpp_:147:3: error: 'cs' was not declared in this scope

display_source_code___cpp_.ino:147:3: note: suggested alternative: 'cos'

display_source_code___cpp_:148:3: error: 'rst' was not declared in this scope

display_source_code___cpp_:149:3: error: 'dc' was not declared in this scope

display_source_code___cpp_:150:3: error: 'sclk' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:150:3: note: suggested alternative: 'cli'

display_source_code___cpp_:151:3: error: 'sid' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:151:3: note: suggested alternative: 'sin'

display_source_code___cpp_:152:3: error: 'hwSPI' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:152:3: note: suggested alternative: 'SPI'

\tic_tac\display_source_code___cpp_.ino: In constructor 'Adafruit_SSD1306::Adafruit_SSD1306(int8_t, int8_t, int8_t)':

display_source_code___cpp_:157:3: error: 'dc' was not declared in this scope

display_source_code___cpp_:158:3: error: 'rst' was not declared in this scope

display_source_code___cpp_:159:3: error: 'cs' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:159:3: note: suggested alternative: 'cos'

display_source_code___cpp_:160:3: error: 'hwSPI' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:160:3: note: suggested alternative: 'SPI'

\tic_tac\display_source_code___cpp_.ino: In constructor 'Adafruit_SSD1306::Adafruit_SSD1306(int8_t)':

display_source_code___cpp_:166:3: error: 'sclk' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:166:3: note: suggested alternative: 'cli'

display_source_code___cpp_:166:10: error: 'dc' was not declared in this scope

display_source_code___cpp_:166:15: error: 'cs' was not declared in this scope

tic_tac\display_source_code___cpp_.ino:166:15: note: suggested alternative: 'cos'

display_source_code___cpp_:166:20: error: 'sid' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:166:20: note: suggested alternative: 'sin'

display_source_code___cpp_:167:3: error: 'rst' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:167:3: note: suggested alternative: 'reset'

\tic_tac\display_source_code___cpp_.ino: At global scope:

display_source_code___cpp_:171:6: error: prototype for 'void Adafruit_SSD1306::begin(uint8_t, uint8_t, bool)' does not match any in class 'Adafruit_SSD1306'

In file included from \tic_tac\tic_tac.ino:9:0:

Documents\Arduino\libraries\Adafruit_SSD1306/Adafruit_SSD1306.h:148:8: error: candidate is: bool Adafruit_SSD1306::begin(uint8_t, uint8_t, bool, bool)

   bool begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = 0,

        ^~~~~

display_source_code___cpp_:293:6: error: prototype for 'void Adafruit_SSD1306::invertDisplay(uint8_t)' does not match any in class 'Adafruit_SSD1306'

In file included from \tic_tac\tic_tac.ino:9:0:

\Documents\Arduino\libraries\Adafruit_SSD1306/Adafruit_SSD1306.h:152:8: error: candidate is: virtual void Adafruit_SSD1306::invertDisplay(bool)

   void invertDisplay(bool i);

        ^~~~~~~~~~~~~

\tic_tac\display_source_code___cpp_.ino: In member function 'void Adafruit_SSD1306::ssd1306_command(uint8_t)':

display_source_code___cpp_:302:7: error: 'sid' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:302:7: note: suggested alternative: 'sin'

display_source_code___cpp_:306:6: error: 'csport' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:306:6: note: suggested alternative: 'csPort'

display_source_code___cpp_:306:16: error: 'cspinmask' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:306:16: note: suggested alternative: 'csPinMask'

display_source_code___cpp_:307:6: error: 'dcport' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:307:6: note: suggested alternative: 'dcPort'

display_source_code___cpp_:307:17: error: 'dcpinmask' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:307:17: note: suggested alternative: 'dcPinMask'

display_source_code___cpp_:314:5: error: 'fastSPIwrite' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:314:5: note: suggested alternative: 'SPIwrite'

display_source_code___cpp_:325:28: error: '_i2caddr' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:325:28: note: suggested alternative: 'i2caddr'

\tic_tac\display_source_code___cpp_.ino: In member function 'void Adafruit_SSD1306::dim(boolean)':

display_source_code___cpp_:409:9: error: '_vccstate' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:409:9: note: suggested alternative: 'vccstate'

\tic_tac\display_source_code___cpp_.ino: In member function 'void Adafruit_SSD1306::display()':

display_source_code___cpp_:438:7: error: 'sid' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:438:7: note: suggested alternative: 'sin'

display_source_code___cpp_:442:6: error: 'csport' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:442:6: note: suggested alternative: 'csPort'

display_source_code___cpp_:442:16: error: 'cspinmask' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:442:16: note: suggested alternative: 'csPinMask'

display_source_code___cpp_:443:6: error: 'dcport' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:443:6: note: suggested alternative: 'dcPort'

display_source_code___cpp_:443:16: error: 'dcpinmask' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:443:16: note: suggested alternative: 'dcPinMask'

display_source_code___cpp_:452:7: error: 'fastSPIwrite' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:452:7: note: suggested alternative: 'SPIwrite'

display_source_code___cpp_:474:30: error: '_i2caddr' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino:474:30: note: suggested alternative: 'i2caddr'

display_source_code___cpp_:475:7: error: 'WIRE_WRITE' was not declared in this scope

\tic_tac\display_source_code___cpp_.ino: At global scope:

display_source_code___cpp_:495:53: error: no 'void Adafruit_SSD1306::fastSPIwrite(uint8_t)' member function declared in class 'Adafruit_SSD1306'

exit status 1

'cs' was not declared in this scope


When a .H library "has errors" it is probably because, inside that library file, it references another library file that can not be seen by the IDE (missing, moved, renamed, et c.). Each library file will have a .CPP file, usually in the same directory as the .H file. So, look in the .H files for references to other .CPP files.

I will say this now, but advise to save it for "when all else fails" because I think this long search for the solution is a way to learn how the IDE is put together, and the things you need to make it work. BUT, WHEN ALL ELSE FAILS... fully uninstall your Arduino IDE, reboot your computer, get the most recent Arduino IDE (internet: arduino.cc >> SOFTWARE >> DOWNLOADS >> find it there) and then after the install is complete, reboot, then use IDE Library Manager (SKETCH >> LIBRARIES >> MANAGE LIBRARIES >>) to update all your libraries.

I just compiled your code. The first error for me was concerning Adafruit_SSD1306.H so I opened the Library Manager, pasted Adafruit_SSD1306 and installed the first occurrence. That cleared my Adafruit_SSD1306.H error... and I will continue to look in the errors for files missing, and use the Library Manage to install them.

After some poking in Adafruit_SSD1306.H a reference to "deprecated" code, calls for "sclk" seem to be replaced with "sclk_pin"... same with cs (chip select), and probably the other errors you are seeing.

// copy/paste from Adafruit_SSD1306.H
class Adafruit_SSD1306 : public Adafruit_GFX {
public:
  // NEW CONSTRUCTORS -- recommended for new projects
  Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
                   int8_t rst_pin = -1, uint32_t clkDuring = 400000UL,
                   uint32_t clkAfter = 100000UL);
  Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin, int8_t sclk_pin,
                   int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
  Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi, int8_t dc_pin,
                   int8_t rst_pin, int8_t cs_pin, uint32_t bitrate = 8000000UL);

  // DEPRECATED CONSTRUCTORS - for back compatibility, avoid in new projects
  Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin, int8_t dc_pin,
                   int8_t rst_pin, int8_t cs_pin);
  Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
  Adafruit_SSD1306(int8_t rst_pin = -1);
// end of copy/paste

So... update your code?

Tried the "serial monitor" code. Passed.

I bought a .96" OLED.
Tried the "without resistor" code. Failed.

Our errors are probably the same, and probably an out-dated .H file, as the .INO was uploaded 2022.

https://create.arduino.cc/projecthub/giobbino/easiest-tictactoe-with-and-without-an-oled-display-f06231

The FILE >> EXAMPLES >> Adafruit1306 >> (screensize) worked.

The following page also shows that text and images work:

Time to look again at tictactoe and find where the code is broken.

====================

I have to post on the end of this post because the rules will not let me post "three consecutive times."

====================

And the answer is... the dude wrote a .H and .CPP that were for another screen or at least, not for the SSD1306. I even think that the #include <SPI.h> is unnecessary, as I looked at Arduino's OLED tutorial, all they use is Wire.h, Adafruit_GFX.h and Adafruit_SSD1306.h to make it work. So... do not download all of his files. Just get the one you want (with resistor or without resistor) and delete all the other files in your tictactoe directory. Let me know if that solves the issue... and if it did, click "Solution"

So, I tried everything in the beginning without those files - it didn't, so i added them - it didn't work - i wrote here.

The problem seems to be with protheus, because code itself works fine in online simulator. So, I thought maybe the issue is in the protheus scheme itself

Even simple codes from internet (like line drawing etc.) they compile in arduino, but show nothing on display in protheus