The Inbox: TicTacToe-Game-MJK.5.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: TicTacToe-Game-MJK.5.mcz

commits-2
A new version of TicTacToe-Game was added to project The Inbox:
http://source.squeak.org/inbox/TicTacToe-Game-MJK.5.mcz

==================== Summary ====================

Name: TicTacToe-Game-MJK.5
Author: MJK
Time: 6 January 2009, 12:30:01 am
UUID: d6b2b9aa-8ea7-c444-a102-eeedf20d219d
Ancestors: TicTacToe-Game-MJK.4

Added window title.

==================== Snapshot ====================

SystemOrganization addCategory: #'TicTacToe-Game'!

BorderedMorph subclass: #TTTBoard
        instanceVariableNames: 'board logic cellWidth cellHeight win'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TicTacToe-Game'!

!TTTBoard commentStamp: 'MJK 1/5/2009 03:05' prior: 0!
Implements a game board and connects game logic and all pieces of the board together. Use command:

        TTTBoard new openInWindow.

To start the game.!

----- Method: TTTBoard>>initialize (in category 'initialization') -----
initialize

        super initialize.
       
        logic := TTTLogic new.

        cellHeight := 64.
        cellWidth := 64.
       
        self borderWidth: 2.
        self borderColor: Color black.
        self color: Color darkGray.
        self height: cellHeight * 3 + 4.
        self width: cellWidth * 3 + 4.
        board := Matrix new: 3 tabulate: [:y :x | self setCellAt: y at: x logic: logic].
        logic board: board.
!

----- Method: TTTBoard>>openInWindow (in category 'initialization') -----
openInWindow

        win := SystemWindow new.
        win setLabel: 'Tic Tac Toe'.
        win addMorph: self frame: (0.0 @ 0.0 extent: 1.0 @ 1.0).
        win openInWorld.
        win width: 64 * 3 + 4 + 10.
        win height: 64 * 3 + 4 + 30.
        win removeCornerGrips.
!

----- Method: TTTBoard>>setCellAt:at:logic: (in category 'helper') -----
setCellAt: y at: x logic: gameLogic
        "Creates a new cell object for given location and sends game logic handler to the cell."

        | c |

        logic := gameLogic.
       
        c := TTTCell new.
        c width: cellWidth.
        c height: cellHeight.
        c logic: logic.
        self addMorph: c.
        c position: ((x - 1) * c width + 2) @ ((y - 1) * c height + 2).
        c addMouseUpActionWith: 'logic actionAt:', y printString, ' at: ', x printString.

        ^c.
!

BorderedMorph subclass: #TTTCell
        instanceVariableNames: 'cellValue logic'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TicTacToe-Game'!

!TTTCell commentStamp: 'MJK 10/11/2008 23:08' prior: 0!
Implements a cell component for the game board.!

----- Method: TTTCell>>initialize (in category 'initialization') -----
initialize

        super initialize.
        self borderWidth: 1.
        self borderColor: Color black.
        self color: Color lightGray.
        self value: 0.
!

----- Method: TTTCell>>logic (in category 'accessing') -----
logic

        ^logic.
!

----- Method: TTTCell>>logic: (in category 'accessing') -----
logic: gameLogic

        logic := gameLogic.
!

----- Method: TTTCell>>value (in category 'accessing') -----
value

        ^cellValue.
!

----- Method: TTTCell>>value: (in category 'accessing') -----
value: value
        "Set value to 0 to clear the cell.
         Set value to 1 to set human player move.
         Set value to 10 to set computer player move."
       
        cellValue := value.
        (value = 1) ifTrue: [self color: Color blue].
        (value = 10) ifTrue: [self color: Color red].
        (value = 100) ifTrue: [self color: Color black].
        (value = 0) ifTrue: [self color: Color lightGray].
!

Object subclass: #TTTLogic
        instanceVariableNames: 'board'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'TicTacToe-Game'!

!TTTLogic commentStamp: 'MJK 10/11/2008 23:09' prior: 0!
The game logic. Includes management for moves made by a human and computer player and also takes care of checking the game state and handles computer moves.!

----- Method: TTTLogic>>actionAt:at: (in category 'game logic') -----
actionAt: y at: x
        "Accept user move, check if the game is over, make machine move."

        (self isCellEmptyAt: y at: x) ifFalse: [^false].

        self cellValue: 1 at: y at: x.

        (self checkVictory) ifTrue: [^true].
       
        (self doComputerMove) ifFalse: [self setVinner: 100. ^false].

        (self checkVictory) ifTrue: [^true].
!

----- Method: TTTLogic>>board (in category 'accessing') -----
board

        ^board.
!

----- Method: TTTLogic>>board: (in category 'accessing') -----
board: gameBoard

        board := gameBoard.
!

----- Method: TTTLogic>>calculateCellValueAt:at: (in category 'game logic') -----
calculateCellValueAt: y at: x
        "Calculates cell value for a given cell. This value determines, which cell is the best
         for the next move made by the computer."

        "
        Rules:
       
        Returns...
        4 points: computer will have a full row with this move
        3 points: human will have a full row with this move
        2 points: computer will have a full row in two moves
        1 point: everything else
        0 points: cell not available
        "

        | i |

        "Check if the cell is already taken."
        (self isCellEmptyAt: y at: x) ifFalse: [^0].
       
        ((x = 1) & (y = 1)) ifTrue: [
                i := (self pickLargestOf: (self calculateDiagonal: 1) of: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 2) & (y = 1)) ifTrue: [
                i := (self pickLargestOf: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 3) & (y = 1)) ifTrue: [
                i := (self pickLargestOf: (self calculateDiagonal: -1) of: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 1) & (y = 2)) ifTrue: [
                i := (self pickLargestOf: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 2) & (y = 2)) ifTrue: [
                i := (self pickLargestOf: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 3) & (y = 2)) ifTrue: [
                i := (self pickLargestOf: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 1) & (y = 3)) ifTrue: [
                i := (self pickLargestOf: (self calculateDiagonal: -1) of: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 2) & (y = 3)) ifTrue: [
                i := (self pickLargestOf: (self calculateRow: y) of: (self calculateColumn: x))
        ].
        ((x = 3) & (y = 3)) ifTrue: [
                i := (self pickLargestOf: (self calculateDiagonal: 1) of: (self calculateRow: y) of: (self calculateColumn: x))
        ].

        (i = 20) ifTrue: [^4]. "Computer may win at next move!!"
        (i = 2) ifTrue: [^4]. "Human may win at next move!!"
        (i = 10) ifTrue: [^3]. "Computer has one, human has none."
        (i = 1) ifTrue: [^2]. "Human has one at otherwise empty row."
        (i = 11) ifTrue: [^1]. "Human has one, computer has one."

        ^1.
!

----- Method: TTTLogic>>calculateColumn: (in category 'helper') -----
calculateColumn: x
        "Adds all cells from the given column and returns the result."

        | result |

        ((x < 1) | (x > 3)) ifTrue: [^-1].

        result := (self cellValueAt: 1 at: x) + (self cellValueAt: 2 at: x) + (self cellValueAt: 3 at: x).
       
        ^result.
!

----- Method: TTTLogic>>calculateDiagonal: (in category 'helper') -----
calculateDiagonal: i
        "Adds all cells diagonally and returns the result.
        i = 1 --> from top left to lower right
        i = -1 --> from top right to lower left,"

        | result |

        result := -1.

        (i = 1) ifTrue: [result := (self cellValueAt: 1 at: 1) + (self cellValueAt: 2 at: 2) + (self cellValueAt: 3 at: 3)].
        (i = -1) ifTrue: [result := (self cellValueAt: 1 at: 3) + (self cellValueAt: 2 at: 2) + (self cellValueAt: 3 at: 1)].

        ^result.
!

----- Method: TTTLogic>>calculateRow: (in category 'helper') -----
calculateRow: y
        "Adds all cells from the given row and returns the result."

        | result |

        ((y < 1) | (y > 3)) ifTrue: [^-1].

        result := (self cellValueAt: y at: 1) + (self cellValueAt: y at: 2) + (self cellValueAt: y at: 3).
       
        ^result.
!

----- Method: TTTLogic>>cellValue:at:at: (in category 'accessing') -----
cellValue: value at: y at: x

        (board at: y at: x) value: value.
!

----- Method: TTTLogic>>cellValueAt:at: (in category 'accessing') -----
cellValueAt: y at: x

        | retval |
       
        retval := (board at: y at: x) value.
       
        ^retval.
!

----- Method: TTTLogic>>checkVictory (in category 'game logic') -----
checkVictory

        | result |
       
        "8 different possibilities for victory:"

        "Rows:"
       
         1 to: 3 do: [:j |
                result := 0.
                result := self calculateRow: j.
                (result = 3) ifTrue: [self setVinner: 1. ^true].
                (result = 30) ifTrue: [self setVinner: 10. ^true].
        ].

        "Columns:"
        1 to: 3 do: [:i |
                result := 0.
                result := self calculateColumn: i.
                (result = 3) ifTrue: [self setVinner: 1. ^true].
                (result = 30) ifTrue: [self setVinner: 10. ^true].
        ].

        "Diagonals:"
        result := 0.
        result := self calculateDiagonal: 1.
        (result = 3) ifTrue: [self setVinner: 1. ^true].
        (result = 30) ifTrue: [self setVinner: 10. ^true].
        result := 0.
        result := self calculateDiagonal: -1.
        (result = 3) ifTrue: [self setVinner: 1. ^true].
        (result = 30) ifTrue: [self setVinner: 10. ^true].

        ^false
!

----- Method: TTTLogic>>doComputerMove (in category 'game logic') -----
doComputerMove
        "This calculates the best possible move for the computer."

        | x y result pointMatrix |

        pointMatrix := Matrix new: 3 tabulate: [:j :i | self calculateCellValueAt: j at: i].

        result := 0.
        x := -1.
        y := -1.

        1 to: 3 do: [:j |
                1 to: 3 do: [:i |
                        ((pointMatrix at: j at: i) > result) ifTrue: [
                                result := pointMatrix at: j at: i.
                                x := i.
                                y := j.]
                ].
        ].

        ((x = -1) | (y = -1)) ifTrue: [^false].

        self cellValue: 10 at: y at: x.

        ^true.
!

----- Method: TTTLogic>>isCellEmptyAt:at: (in category 'helper') -----
isCellEmptyAt: y at: x

        ((board at: y at: x) value == 0) ifTrue: [^true].
       
        ^false.
!

----- Method: TTTLogic>>isEmptyCells (in category 'helper') -----
isEmptyCells

        | result |

        result := false.
               
        1 to: 3 do: [:y |
                1 to: 3 do: [:x |
                        (self isCellEmptyAt: y at: x) ifTrue: [result := true].
                ].
        ].

        ^result.
!

----- Method: TTTLogic>>pickLargestOf:of: (in category 'helper') -----
pickLargestOf: a of: b
        "Returns the largest value of the given a, b and c."

        "Exception: if human is winning (2 == opponent has two cells in a row) then forget
        strategy and try to stop it."
        ((a = 2) | (b = 2)) ifTrue: [^2].

        (a > b) ifTrue: [^a].
        (b > a) ifTrue: [^b].

        ^a.
!

----- Method: TTTLogic>>pickLargestOf:of:of: (in category 'helper') -----
pickLargestOf: a of: b of: c
        "Returns the largest value of the given a, b and c."

        "Exception: if human is winning (2 == opponent has two cells in a row) then forget
        strategy and try to stop it."
        ((a = 2) | (b = 2) | (c = 2)) ifTrue: [^2].
       
        (a > b) ifTrue: [(a > c) ifTrue: [^a] ifFalse: [^c]] ifFalse: [(b > c) ifTrue: [^b] ifFalse: [^c]].
        (b > c) ifTrue: [(b > a) ifTrue: [^b] ifFalse: [^a]] ifFalse: [(c > a) ifTrue: [^c] ifFalse: [^a]].
        (c > a) ifTrue: [(c > b) ifTrue: [^c] ifFalse: [^b]] ifFalse: [(a > b) ifTrue: [^a] ifFalse: [^b]].
       
        ^a.
!

----- Method: TTTLogic>>setVinner: (in category 'helper') -----
setVinner: value

        1 to: 3 do: [:y |
                1 to: 3 do: [:x |
                        (board at: y at: x) value: value.
                ].
        ].
!