class Hash
def next(key)
keys = self.keys
pos = keys.index(key)
if pos+1 == keys.length
return keys[0]
else
return keys[pos+1]
end
end
end
class Pousse
def initialize(size = 4)
@size = size.to_i.freeze
@board = Array.new(@size){ Array.new(@size) }
@previous = []
@players = {:one => 'X', :two => 'O'}.freeze
run
end
def victory?
grand_total = Hash.new(0)
inv_players = @players.invert
@board.each_index do |x|
r_sub, c_sub = Hash.new(0), Hash.new(0)
@board[x].each_index do |y|
r_sub[inv_players[@board[x][y]]] += 1 if @board[x][y]
c_sub[inv_players[@board[y][x]]] += 1 if @board[y][x]
end
[c_sub, r_sub].each do |a|
a.each do |x|
grand_total[x[0]] += 1 if x[1] == @size
end
end
end
if grand_total.values.inject{|sum, n| sum+n}
if grand_total[@player] == grand_total[@players.next(@player)]
puts "It's a tie!"
elsif grand_total[@player] > grand_total[@players.next(@player)]
puts "Player #{@player.to_s}, you win!"
return true
else
puts "Player #{@players.next(@player)}, you win!"
return true
end
end
@previous.each do |x|
if x == @board
puts "Player #{@player.to_s}, that board is a REPEAT! YOU LOSE!"
return true
end
end
@previous << Marshal.load(Marshal.dump(@board))
false
end
def display_help
puts "--------"
puts "General commands:
h - display this help dialog
d - draw the board
q - quit the program"
puts "move? commands (i is an integer from 1 to #{@size})
Li - Left
Ri - Right
Ti - Top
Bi - Bottom"
puts "--------"
end
def draw_board
print "\n "
@size.times do |x|
print " #{x+1}"
end
puts ""
(@size * 2).times do |x|
if (x % 2 == 0)
print " "
@size.times { print "+-" }
puts "+"
else
print "#{x/2+1} "
@size.times do |y|
print "|"
print @board[x/2][y] ? @board[x/2][y] : " "
end
puts "|"
end
end
print " "
@size.times { print "+-" }
puts "+"
end
def move?(command)
direction, row = command.split(//)
row = row.to_i
if row > @size or row < 1
puts "Invalid index value (#{row}) supplied."
return false
end
row -= 1
case direction.upcase
when "L" then x = row; y = 0; dx = 0; dy = 1
when "R" then x = row; y = @size-1; dx = 0; dy = -1
when "T" then x = 0; y = row; dx = 1; dy = 0
when "B" then x = @size-1; y = row ; dx = -1; dy = 0
end
shift_board(x, y, dx, dy, @players[@player])
true
end
def run
puts "Welcome to Pousse!"
display_help
@player = :one
loop do
puts "Player #{@player.to_s}'s (#{@players[@player]}) turn."
print "> "
_input = $stdin.gets
if _input =~ /^[q|Q]/ then break
elsif _input =~ /^[h|H]/ then display_help
elsif _input =~ /^[d|D]/ then draw_board
elsif _input =~ /^[L|R|T|B|l|r|t|b][\d]/ then
if move?(_input)
draw_board
break if victory?
@player = @players.next(@player)
end
else puts "Command not recognized."
end
end
puts "Thanks for playing Pousse!"
end
def shift_board(to_x, to_y, dx, dy, append)
if to_y < @size and to_x < @size then
if @board[to_x][to_y] != nil then
mv = @board[to_x][to_y]
@board[to_x][to_y] = nil
shift_board(to_x+dx, to_y+dy, dx, dy, mv)
end
@board[to_x][to_y] = append
end
end
end
if ARGV[0]
Pousse.new(ARGV[0])
else
Pousse.new
end