How to resolve the algorithm Tic-tac-toe step by step in the Lingo programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Tic-tac-toe step by step in the Lingo programming language

Table of Contents

Problem Statement

Play a game of tic-tac-toe. Ensure that legal moves are played and that a winning position is notified.

Tic-tac-toe   is also known as:

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Tic-tac-toe step by step in the Lingo programming language

Source code in the lingo programming language

global $ -- object representing simple framework
global gBoard -- current board image
global gBoardTemplate -- empty board image
global gHumanChip -- cross image
global gComputerChip -- circle image
global gM -- 3x3 matrix storing game state: 0=free cell, 1=human cell, -1=computer cell
global gStep -- index of current move (1..9)
global gGameOverFlag -- TRUE if current game is over

----------------------------------------
-- Entry point
----------------------------------------
on startMovie

    -- libs
    $.import("sprite")

    -- window properties
    _movie.stage.title = "Tic-Tac-Toe"
    _movie.stage.rect = rect(0, 0, 224, 310)
    _movie.centerStage = TRUE

    -- load images from filesystem
    m = new(#bitmap)
    m.importFileInto($.@("resources/cross.bmp"), [#trimWhiteSpace:FALSE])
    gHumanChip = m.image

    m = new(#bitmap)
    m.importFileInto($.@("resources/circle.bmp"), [#trimWhiteSpace:FALSE])
    gComputerChip = m.image

    -- create GUI
    m = new(#bitmap)
    m.importFileInto($.@("resources/board.bmp"))
    m.regpoint = point(0, 0)
    s = $.sprite.make(m, [#loc:point(20, 20)], TRUE)
    s.addListener(#mouseDown, _movie, #humanMove)

    gBoard = m.image
    gBoardTemplate = gBoard.duplicate()

    m = $.sprite.newMember(#button, [#text:"New Game (Human starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)])
    s = $.sprite.make(m, [#loc:point(20, 220)], TRUE)
    s.addListener(#mouseDown, _movie, #newGame, 1)

    m = $.sprite.newMember(#button, [#text:"New Game (Computer starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)])
    s = $.sprite.make(m, [#loc:point(20, 250)], TRUE)
    s.addListener(#mouseDown, _movie, #newGame, -1)

    m = $.sprite.newMember(#field, [#name:"feedback", #editable:FALSE, #fontstyle:"bold", #alignment:"center",\
        #border:0, #color:rgb(255, 0, 0), #rect:rect(0, 0, 180, 0)])
    s = $.sprite.make(m, [#loc:point(20, 280)], TRUE)

    newGame(1)

    -- show the application window
    _movie.updateStage()
    _movie.stage.visible = TRUE
end

----------------------------------------
-- Starts a new game
----------------------------------------
on newGame (whoStarts)
    -- reset board
    gBoard.copyPixels(gBoardTemplate, gBoardTemplate.rect, gBoardTemplate.rect)
    -- clear feedback
    member("feedback").text = ""
    -- reset states
    gM = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    gStep = 0
    gGameOverFlag = FALSE
    if whoStarts=-1 then computerMove()
end

----------------------------------------
-- Handles a human move (mouse click)
----------------------------------------
on humanMove ()
    if gGameOverFlag then return
    -- find cell for mouse position
    p = _mouse.clickLoc - sprite(1).loc
    if p.locH mod 60<4 or p.locV mod 60<4 then return
    p = p / 60
    x = p[1] + 1
    y = p[2] + 1
    if gM[x][y] then return -- ignore illegal moves
    gM[x][y] = 1
    -- update cell image
    p = p * 60
    gBoard.copyPixels(gHumanChip, gHumanChip.rect.offset(4+p[1], 4+p[2]), gHumanChip.rect)
    -- proceed (unless game over)
    gStep = gStep + 1
    if not checkHumanMove(x, y) then computerMove()
end

----------------------------------------
-- Checks if human has won or game ended with draw
----------------------------------------
on checkHumanMove (x, y)
    if sum([gM[x][1], gM[x][2], gM[x][3]])=3 then return gameOver(1, [[x, 1], [x, 2], [x, 3]])
    if sum([gM[1][y], gM[2][y], gM[3][y]])=3 then return gameOver(1, [[1, y], [2, y], [3, y]])
    if x=y and sum([gM[1][1], gM[2][2], gM[3][3]])=3 then return gameOver(1, [[1, 1], [2, 2], [3, 3]])
    if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]])=3 then return gameOver(1, [[1, 3], [2, 2], [3, 1]])
    if gStep=9 then return gameOver(0)
    return FALSE
end

----------------------------------------
-- Checks if selecting specified empty cell makes computer or human win
----------------------------------------
on checkCellWins (x, y, who)
    wins = who*2
    if sum([gM[1][y], gM[2][y], gM[3][y]]) = wins then return [[1, y], [2, y], [3, y]]
    if sum([gM[x][1], gM[x][2], gM[x][3]]) = wins then return [[x, 1], [x, 2], [x, 3]]
    if x=y and sum([gM[1][1], gM[2][2], gM[3][3]]) = wins then return [[1, 1], [2, 2], [3, 3]]
    if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]]) = wins then return [[1, 3], [2, 2], [3, 1]]
    return FALSE
end

----------------------------------------
-- Handles game over
----------------------------------------
on gameOver (winner, cells)
    gGameOverFlag = TRUE
    if winner = 0 then
        member("feedback").text = "It's a draw!"
    else
        -- hilite winning line with yellow
        img = image(56, 56, 32)
        img.fill(img.rect, rgb(255, 255, 0))
        repeat with c in cells
            x = (c[1]-1)*60 + 4
            y = (c[2]-1)*60 + 4
            gBoard.copyPixels(img, img.rect.offset(x, y), img.rect, [#ink:#darkest])
        end repeat
        member("feedback").text = ["Human", "Computer"][1+(winner=-1)] & " has won!"
    end if
    return TRUE
end

----------------------------------------
-- Calculates next computer move
----------------------------------------
on computerMove ()
    gStep = gStep + 1

    -- move 1: select center
    if gStep=1 then return doComputerMove(2, 2)

    -- move 2 (human started)
    if gStep=2 then
        if gM[2][2]=1 then
            -- if center, select arbitrary corner
            return doComputerMove(1, 1)
        else
            -- otherwise select center
            return doComputerMove(2, 2)
        end if
    end if

    -- move 3 (computer started)
    if gStep=3 then
        -- if corner, select diagonally opposite corner
        if gM[1][1]=1 then return doComputerMove(3, 3)
        if gM[3][3]=1 then return doComputerMove(1, 1)
        if gM[1][3]=1 then return doComputerMove(3, 1)
        return doComputerMove(1, 1) -- top left corner as default
    end if

    -- get free cells
    free = []
    repeat with x = 1 to 3
        repeat with y = 1 to 3
            if gM[x][y]=0 then free.add([x, y])
        end repeat
    end repeat

    -- check if computer can win now
    repeat with c in free
        res = checkCellWins(c[1], c[2], -1)
        if res<>FALSE then
            doComputerMove(c[1], c[2])
            return gameOver(-1, res)
        end if
    end repeat

    -- check if human could win with next move (if yes, prevent it)
    repeat with c in free
        res = checkCellWins(c[1], c[2], 1)
        if res<>FALSE then return doComputerMove(c[1], c[2], TRUE)
    end repeat

    -- move 4 (human started): prevent "double mills"
    if gStep=4 then
        if gM[2][2]=1 and (gM[1][1]=1 or gM[3][3]=1) then return doComputerMove(3, 1)
        if gM[2][2]=1 and (gM[1][3]=1 or gM[3][1]=1) then return doComputerMove(1, 1)
        if gM[2][3]+gM[3][2]=2 then return doComputerMove(3, 3)
        if gM[1][2]+gM[2][3]=2 then return doComputerMove(1, 3)
        if gM[1][2]+gM[2][1]=2 then return doComputerMove(1, 1)
        if gM[2][1]+gM[3][2]=2 then return doComputerMove(3, 1)
        if (gM[1][3]+gM[3][1]=2) or (gM[1][1]+gM[3][3]=2) then return doComputerMove(2, 1)
    end if

    -- move 5 (computer started): try to create a "double mill"
    if gStep=5 then
        repeat with x = 1 to 3
            col = [gM[x][1], gM[x][2], gM[x][3]]
            if not (sum(col)=-1 and max(col)=0) then next repeat
            repeat with y = 1 to 3
                row = [gM[1][y], gM[2][y], gM[3][y]]
                if not (sum(row)=-1 and max(row)=0 and gM[x][y]=0) then next repeat
                return doComputerMove(x, y)
            end repeat
        end repeat
    end if

    -- otherwise use first free cell
    c = free[1]
    doComputerMove(c[1], c[2])
end

----------------------------------------
-- Updates state matrix and cell image
----------------------------------------
on doComputerMove (x, y, checkDraw)
    gM[x][y] = -1
    gBoard.copyPixels(gComputerChip, gComputerChip.rect.offset(4+(x-1)*60, 4+(y-1)*60), gComputerChip.rect)
    if checkDraw and gStep=9 then gameOver(0)
end

----------------------------------------
--
----------------------------------------
on sum (aLine)
    return aLine[1]+aLine[2]+aLine[3]
end

  

You may also check:How to resolve the algorithm Jaro-Winkler distance step by step in the jq programming language
You may also check:How to resolve the algorithm Matrix multiplication step by step in the Seed7 programming language
You may also check:How to resolve the algorithm Array length step by step in the Nanoquery programming language
You may also check:How to resolve the algorithm Prime decomposition step by step in the AWK programming language
You may also check:How to resolve the algorithm Bernoulli numbers step by step in the Factor programming language