[EN] ESP32-ML4M : Tic-Tac-Toe Ep.2

From the article Tic-Tac-Toe games or OC games played with computers (esp32) via the console of the terminal program are inconvenient. Now let’s learn how to use the esp32 peripheral circuit of the ML4M board (Figure 1), how it has an I/O circuit and how is Tic-Tac-Toe game is played through the device in which the code works with the joystick module and touch switch.

(Figure. 1 ml4m for Tic-Tac-Toe testing)

Equipment

The designed ml4m board requires the following equipment:

  1. Joystick
  2. Touch switch
  3. OLED
  4. Buzzer

The circuit diagram of the I/O on the board is shown in Figure 2.

(Figure. 2 Connection diagram with Joystick/Switch)

Buzzer

The buzzer is a sound device. It is connected to DAC1 or Pin GPIO15 as shown in Figure 3.

(Figure. 3 Buzzer)

Joystick

The joystick module connects VRX and VRY pins to ADC pins VP and VN as shown in Figure 4.

(Figure. 4 Connection with joystick)

Switch

There are four switches used on the board, one push-button switch connected by pull-up and three touch switches connected to pin GPIO 32,33,34 and 35 as shown in Figure 5.

(Figure. 5 Connection with switches)

Program

An example of a Tic-Tac-Toe program played through the joystick module and the swB button for a channel selection button is as follows. The working description has already been inserted in the comment section.

#######################################################
### Tic-Tac-Toe
### board: ML4M
### (C) 2021, JarutEx
### www.jarutex.com
#######################################################
from machine import Pin,I2C,ADC
import math
import machine
import gc
import ssd1306
import random
import time

#######################################################
## System setting
gc.enable()
gc.collect()
machine.freq(240000000)

#######################################################
## OLED
sclPin = Pin(22)
sdaPin = Pin(21)
i2c = I2C(0,scl=sclPin, sda=sdaPin, freq=400000)
oled = ssd1306.SSD1306_I2C(128,64,i2c)
oled.poweron()
oled.contrast(255)
oled.init_display()
oled.fill(0)
oled.show()

## constant
blockHeight = const(16)
blockWidth = const(16)
screenWidth = const(128)
screenHeight = const(64)

## Game pad
swA = Pin(32, Pin.IN)
swB = Pin(33, Pin.IN)
swC = Pin(35, Pin.IN)
swD = Pin(34, Pin.IN)
swF = Pin(26, Pin.IN) # select
swE = Pin(27, Pin.IN) # start
jst = (ADC(Pin(39)), ADC(Pin(36))) # X,Y
jst[0].width( ADC.WIDTH_12BIT ) # 12bit
jst[0].atten( ADC.ATTN_11DB ) # 3.3V
jst[1].width( ADC.WIDTH_12BIT ) # 12bit
jst[1].atten( ADC.ATTN_11DB ) # 3.3V


## Gobal variables
table = ["","","","","","","","",""]
userPos = 0


#######################################################
## drawTable()
## แสดงตารางของเกม
#######################################################
def drawTable():
    oled.fill(0)
    left = (screenWidth - (blockWidth*3))//2
    top = (screenHeight - (blockHeight*3))//2
    for i in range(4):
        oled.hline(left-(blockWidth//4),(top-(blockHeight//4))+blockHeight*i, blockWidth*3,1)
        oled.vline((left-(blockWidth//4))+(i*blockWidth),top-(blockHeight//4), blockWidth*3,1)
    i = 0
    for r in range(3):
        for c in range(3):
            oled.text(str(table[i]),left+blockWidth*c,top+blockHeight*r)
            i += 1
    oled.show()
    
#######################################################
## getInput()
## รับค่าจาก Joystick และ button switch
#######################################################
def getInput():
    left = False
    right = False
    up = False
    down = False
    button = False
    jx = 4095-jst[0].read()
    jy = jst[1].read()
    
    if (jx < 1200):
        left = True
    elif (jx > 3000):
        right = True
    if (jy < 1200):
        up = True
    elif (jy > 3000):
        down = True
    b = swB.value()
    if (b):
        t0 = time.ticks_ms()
        time.sleep_ms(100)
        b2 = swB.value()
        if (b == b2):
            button = True
    return (left,right,up,down,button)

#######################################################
## isWin(p)
## Check if p meets the winning conditions or not.
#######################################################
def isWin(p):
    win = False
    # case 1 : 1st row is p
    if ((table[0]==p)and(table[1]==p)and(table[2]==p)):
        win = True
    # case 2 : 2nd row is p
    if ((table[3]==p)and(table[4]==p)and(table[5]==p)):
        win = True
    # case 3 : 3rd row is p
    if ((table[6]==p)and(table[7]==p)and(table[8]==p)):
        win = True
    # case 4 : column 0 is p
    if ((table[0]==p)and(table[3]==p)and(table[6]==p)):
        win = True
    # case 5 : column 1 is p
    if ((table[1]==p)and(table[4]==p)and(table[7]==p)):
        win = True
    # case 6 : column 2 is p
    if ((table[2]==p)and(table[5]==p)and(table[8]==p)):
        win = True
    # case 7 : \ angle
    if ((table[0]==p)and(table[4]==p)and(table[8]==p)):
        win = True
    # case 8 : / angle
    if ((table[2]==p)and(table[4]==p)and(table[6]==p)):
        win = True
    return win    


#######################################################
## testControl2()
## Test move the square box with the lever and exit with swB.
#######################################################
posX=0
posY=0
xResponse = const(2)
yResponse = const(2)
def testControl2():
    global table, posX, posY
    x = (posX<<xResponse)
    y = (posY<<yResponse)
    while True:
        ## clear screen
        oled.fill(0)
        ## draw table
        left = (screenWidth - (blockWidth*3))//2
        top = (screenHeight - (blockHeight*3))//2
        for i in range(4):
            oled.hline(left-(blockWidth//4),(top-(blockHeight//4))+blockHeight*i, blockWidth*3,1)
            oled.vline((left-(blockWidth//4))+(i*blockWidth),top-(blockHeight//4), blockWidth*3,1)
        ## วาดตัวเลขช่อง
        i = 0
        for r in range(3):
            for c in range(3):
                if ((r == (y>>yResponse)) and (c == (x>>yResponse))): # draw solid square
                    oled.fill_rect(left-4+blockWidth*c,top-4+blockHeight*r,
                                   blockWidth, blockHeight,1)
                    oled.text(str(table[i]),left+blockWidth*c,top+blockHeight*r,0)
                else:
                    oled.text(str(table[i]),left+blockWidth*c,top+blockHeight*r,1)
                i+=1
        ## วาด
        oled.show()
        ## input
        (a,d,w,s,n) = getInput()
        #print(a,d,w,s,n)
        if (n):
            x2 = (x>>xResponse)
            y2 = (y>>yResponse)
            pos = (y2*3)+x2
            #print(x2,y2,pos)
            if (table[pos] == ""):
                table[pos] = "O"
                posX = x2
                posY = y2
                break
        ### Update value
        if (a):
            x-=1
        if (d):
            x+=1
        if (w):
            y-=1
        if (s):
            y+=1
        if (x < 0):
            x = 0
        if (y < 0):
            y = 0
        if (x > 2*(1<<xResponse)): # grid * response
            x = 2*(1<<xResponse)
        if (y > 2*(1<<yResponse)): # grid * response
            y = 2*(1<<yResponse)
        time.sleep_ms(20)

#######################################################
### Main program
#######################################################
doAgain = True
while doAgain:
    try:
        counter = 0
        while True:
            # input
            testControl2()
            drawTable()
            # check if human win ?
            if (isWin("O")):
                for i in range(32):
                    oled.scroll(-1,0)
                    oled.show()
                oled.text("You",80,20)
                oled.text("Win",79,30)
                oled.text("Win",80,30)
                oled.text("Win",81,30)
                oled.show()
                for i in range(10):
                    oled.invert(False)
                    time.sleep_ms(200)
                    oled.invert(True)
                    time.sleep_ms(200)
                break
            # computer's turn
            while True:
                pos = random.getrandbits(8)%9 # สุ่ม 0..8
                if (table[pos] == ""): # if empty
                    table[pos] = "X"
                    break
            drawTable()
            # check if computer win?
            if (isWin("X")):
                for i in range(32):
                    oled.scroll(1,0)
                    oled.show()
                oled.text("You",10,20)
                oled.text("Lost",9,30)
                oled.text("Lost",10,30)
                oled.text("Lost",11,30)
                oled.show()
                for i in range(10):
                    oled.invert(False)
                    time.sleep_ms(200)
                    oled.invert(True)
                    time.sleep_ms(200)
                break
            # counter
            counter += 1
            if (counter == 4):
                break
    except KeyboardInterrupt:
        pass
    finally:
        for i in range(64):
            oled.scroll(0,-2)
            oled.show()
    doAgain = False
oled.poweroff()

An example of the program’s operation is shown in the video below.

Conclusion

From this article, you will find that using a microcontroller board for creating games to practice programming is not difficult and can be applied cross-platform programming to other systems as well. Are you interested? Finally, have fun with programming.

If you want to talk with us, feel free to leave comments below!!

(C) 2020-2021, By Jarut Busarathid and Danai Jedsadathitikul
Updated 2021-12-01