You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebindings/qtruby/rubylib/examples/qt-examples/tictac/tictac.rb

312 lines
6.5 KiB

require 'Qt'
class TicTacButton < TQt::PushButton
attr_accessor :btype
Blank, Circle, Cross = 0, 1, 2
def initialize(p)
super(p)
@btype = Blank
end
def drawButtonLabel(p)
r = rect()
p.setPen( TQt::Pen.new( TQt::white,2 ) ) # set fat pen
if (@btype == Circle)
p.drawEllipse( r.left()+4, r.top()+4, r.width()-8, r.height()-8 )
elsif (@btype == Cross) # draw cross
p.drawLine( r.topLeft() +TQt::Point.new(4,4), r.bottomRight()-TQt::Point.new(4,4))
p.drawLine( r.bottomLeft()+TQt::Point.new(4,-4),r.topRight() -TQt::Point.new(4,-4))
end
super(p)
end
end
class TicTacGameBoard < TQt::Widget
signals 'finished()'
slots 'buttonClicked()'
Init, HumansTurn, HumanWon, ComputerWon, NobodyWon = 0, 1, 2, 3, 4
attr_accessor :state, :computer_starts
def initialize (n, parent)
super(parent)
@state = Init
@nBoard = n
n = n*n
@computer_starts = false
@buttons = Array.new(n)
@btArray = Array.new(n)
grid = TQt::GridLayout.new(self, n, n, 4)
p = TQt::Palette.new(TQt::blue)
for i in (0..n-1)
ttb = TicTacButton.new(self)
ttb.setPalette(p)
ttb.setEnabled(false)
connect(ttb, TQ_SIGNAL('clicked()'), self, TQ_SLOT('buttonClicked()'))
grid.addWidget(ttb, i % @nBoard, i / @nBoard)
@buttons[i] = ttb
@btArray[i] = TicTacButton::Blank
end
end
def newGame
@state = HumansTurn
for i in 0..(@nBoard*@nBoard)-1
@btArray[i] = TicTacButton::Blank
end
if @computer_starts == true
computerMove
else
updateButtons
end
end
def updateButtons
for i in 0..(@nBoard*@nBoard)-1
if @buttons[i].btype != @btArray[i]
@buttons[i].btype = @btArray[i]
end
if @buttons[i].btype == TicTacButton::Blank
@buttons[i].setEnabled(true)
else
@buttons[i].setEnabled(false)
end
@buttons[i].repaint
end
end
def checkBoard
t = 0
row = 0
col = 0
won = false
# check horizontal
for row in 0..@nBoard-1
if won == true
break
end
t = @btArray[row*@nBoard]
if (t == TicTacButton::Blank)
next
end
col = 1
while ( (col < @nBoard) && (@btArray[row*@nBoard+col] == t) )
col += 1
end
if (col == @nBoard)
won = true
end
end
# check vertical
for col in 0..@nBoard-1
if won == true
break
end
t = @btArray[col]
if (t == TicTacButton::Blank)
next
end
row = 1
while ( (row < @nBoard) && (@btArray[row*@nBoard+col] == t) )
row += 1
end
if (row == @nBoard)
won = true
end
end
# check diagonal top left to bottom right
if (won == false)
t = @btArray[0]
if (t != TicTacButton::Blank)
i = 1;
while (i<@nBoard && (@btArray[i*@nBoard+i] == t))
i += 1
end
if (i == @nBoard)
won = true
end
end
end
# check diagonal bottom left to top right
if (won == false)
j = @nBoard-1
i = 0;
t = @btArray[i+j*@nBoard];
if (t != TicTacButton::Blank)
i += 1
j -= 1
while ( (i<@nBoard) && (@btArray[i+j*@nBoard] == t) )
i += 1
j -= 1
end
if (i == @nBoard)
won = true
end
end
end
if (won == false)
# no winner
t = 0
end
t
end
def computerMove
numButtons = @nBoard*@nBoard
altv = Array.new
stopHuman = -1
i = 0
for i in 0..numButtons-1 # try all positions
if @btArray[i] != TicTacButton::Blank # already a piece there
next
end
@btArray[i] = TicTacButton::Cross # test if computer wins
if (checkBoard == @btArray[i]) # computer will win
@state = ComputerWon
stopHuman = -1
break
end
@btArray[i] = TicTacButton::Circle # test if human wins
if (checkBoard == @btArray[i]) # oops...
stopHuman = i # remember position
@btArray[i] = TicTacButton::Blank # restore button
next # computer still might win
end
@btArray[i] = TicTacButton::Blank; # restore button
altv.push(i) # remember alternative
end
if (stopHuman >= 0) # must stop human from winning
@btArray[stopHuman] = TicTacButton::Cross
elsif (i == numButtons-1) # tried all alternatives
if (altv.size > 0) # set random piece
@btArray[altv[rand(altv.size)]] = TicTacButton::Cross
end
if ((altv.size-1) == 0) # no more blanks
@state = NobodyWon
emit finished()
end
end
updateButtons # update buttons
end
def buttonClicked
unless @state == HumansTurn
return
end
at = nil
for i in 0..@buttons.size
if @buttons[i].object_id == sender.object_id
at = i
break
end
end
if @btArray[at] == TicTacButton::Blank
@btArray[at] = TicTacButton::Circle
updateButtons
if (checkBoard == 0)
computerMove
end
s = checkBoard
if (s != 0)
if (s == TicTacButton::Circle)
@state = HumanWon
else
@state = ComputerWon
end
emit finished()
end
end
end
end
class TicTacToe < TQt::Widget
slots 'newGameClicked()', 'gameOver()'
def initialize (boardSize)
super()
l = TQt::VBoxLayout.new(self, 6)
@state_msg = [
'Click Play to start',
'Make your move',
'You won!',
'Computer won!',
'It\'s a draw']
# Create a message label
@message = TQt::Label.new(self)
@message.setFrameStyle((TQt::Frame.WinPanel|TQt::Frame.Sunken))
@message.setAlignment(TQt::AlignCenter)
l.addWidget(@message)
# Create the game board and connect the signal finished()
# to this/self gameOver() slot
@board = TicTacGameBoard.new(boardSize, self)
connect(@board, TQ_SIGNAL('finished()'), self, TQ_SLOT('gameOver()'));
l.addWidget(@board)
# Create a horizontal frame line
line = TQt::Frame.new(self)
line.setFrameStyle(TQt::Frame.HLine|TQt::Frame.Sunken)
l.addWidget(line)
# Create the combo box for deciding who should start
# and connect its clicked() signals to the buttonClicked() slot
@whoStarts = TQt::ComboBox.new(self)
@whoStarts.insertItem('Computer starts')
@whoStarts.insertItem('Human starts')
l.addWidget(@whoStarts);
# Create the push buttons and connect their signals to the right slots
@newGame = TQt::PushButton.new('Play!', self)
connect(@newGame, TQ_SIGNAL('clicked()'), self, TQ_SLOT('newGameClicked()'))
@quit = TQt::PushButton.new('Quit', self)
connect(@quit, TQ_SIGNAL('clicked()'), $qApp, TQ_SLOT('quit()'))
b = TQt::HBoxLayout.new
l.addLayout(b)
b.addWidget(@newGame)
b.addWidget(@quit)
newState()
end
def newState
@message.setText(@state_msg[@board.state])
end
def newGameClicked
if @whoStarts.currentItem == 0
@board.computer_starts = true
else
@board.computer_starts = false
end
@board.newGame()
newState()
end
def gameOver
# Update text box
newState()
end
end