1
2
3
4 class Hash
5
6 def next(key)
7 keys = self.keys
8 pos = keys.index(key)
9 if pos+1 == keys.length
10 return keys[0]
11 else
12 return keys[pos+1]
13 end
14 end
15 end
16
17 class Pousse
18 def initialize(size = 4)
19 @size = size.to_i.freeze
20 @board = Array.new(@size){ Array.new(@size) }
21 @previous = []
22 @players = {:one => 'X', :two => 'O'}.freeze
23
24 run
25 end
26
27
28 def victory?
29 grand_total = Hash.new(0)
30 inv_players = @players.invert
31
32
33 @board.each_index do |x|
34 r_sub, c_sub = Hash.new(0), Hash.new(0)
35 @board[x].each_index do |y|
36 r_sub[inv_players[@board[x][y]]] += 1 if @board[x][y]
37 c_sub[inv_players[@board[y][x]]] += 1 if @board[y][x]
38 end
39
40 [c_sub, r_sub].each do |a|
41 a.each do |x|
42 grand_total[x[0]] += 1 if x[1] == @size
43 end
44 end
45 end
46
47
48 if grand_total.values.inject{|sum, n| sum+n}
49 if grand_total[@player] == grand_total[@players.next(@player)]
50 puts "It's a tie!"
51 elsif grand_total[@player] > grand_total[@players.next(@player)]
52 puts "Player #{@player.to_s}, you win!"
53 return true
54 else
55 puts "Player #{@players.next(@player)}, you win!"
56 return true
57 end
58 end
59
60
61 @previous.each do |x|
62 if x == @board
63 puts "Player #{@player.to_s}, that board is a REPEAT! YOU LOSE!"
64 return true
65 end
66 end
67
68 @previous << Marshal.load(Marshal.dump(@board))
69 false
70 end
71
72
73 def display_help
74 puts "--------"
75 puts "General commands:
76 h - display this help dialog
77 d - draw the board
78 q - quit the program"
79
80 puts "move? commands (i is an integer from 1 to #{@size})
81 Li - Left
82 Ri - Right
83 Ti - Top
84 Bi - Bottom"
85 puts "--------"
86 end
87
88
89 def draw_board
90
91 print "\n "
92 @size.times do |x|
93 print " #{x+1}"
94 end
95 puts ""
96
97
98 (@size * 2).times do |x|
99 if (x % 2 == 0)
100 print " "
101 @size.times { print "+-" }
102 puts "+"
103 else
104 print "#{x/2+1} "
105 @size.times do |y|
106 print "|"
107 print @board[x/2][y] ? @board[x/2][y] : " "
108 end
109 puts "|"
110 end
111 end
112
113
114 print " "
115 @size.times { print "+-" }
116 puts "+"
117 end
118
119
120
121 def move?(command)
122 direction, row = command.split(//)
123 row = row.to_i
124
125 if row > @size or row < 1
126 puts "Invalid index value (#{row}) supplied."
127 return false
128 end
129
130 row -= 1
131
132
133 case direction.upcase
134 when "L" then x = row; y = 0; dx = 0; dy = 1
135 when "R" then x = row; y = @size-1; dx = 0; dy = -1
136 when "T" then x = 0; y = row; dx = 1; dy = 0
137 when "B" then x = @size-1; y = row ; dx = -1; dy = 0
138 end
139 shift_board(x, y, dx, dy, @players[@player])
140 true
141 end
142
143
144 def run
145 puts "Welcome to Pousse!"
146 display_help
147 @player = :one
148 loop do
149 puts "Player #{@player.to_s}'s (#{@players[@player]}) turn."
150
151
152 print "> "
153 _input = $stdin.gets
154
155
156 if _input =~ /^[q|Q]/ then break
157 elsif _input =~ /^[h|H]/ then display_help
158 elsif _input =~ /^[d|D]/ then draw_board
159 elsif _input =~ /^[L|R|T|B|l|r|t|b][\d]/ then
160 if move?(_input)
161 draw_board
162 break if victory?
163 @player = @players.next(@player)
164 end
165 else puts "Command not recognized."
166 end
167 end
168 puts "Thanks for playing Pousse!"
169 end
170
171
172
173 def shift_board(to_x, to_y, dx, dy, append)
174 if to_y < @size and to_x < @size then
175 if @board[to_x][to_y] != nil then
176 mv = @board[to_x][to_y]
177 @board[to_x][to_y] = nil
178 shift_board(to_x+dx, to_y+dy, dx, dy, mv)
179 end
180 @board[to_x][to_y] = append
181 end
182 end
183 end
184
185 if ARGV[0]
186 Pousse.new(ARGV[0])
187 else
188 Pousse.new
189 end