view src/net/kryshen/charamega/game.mirah @ 6:9d940c7ee4c0

Adjusted defaults and limits.
author Mikhail Kryshen <mikhail@kryshen.net>
date Mon, 16 Jul 2012 06:30:28 +0400
parents 28c25c08059d
children cf757c4466b8
line source
1 #
2 # Copyright 2012 Mikhail Kryshen <mikhail@kryshen.net>
3 #
4 # This file is part of Charamega.
5 #
6 # Charamega is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # Charamega is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Charamega. If not, see <http://www.gnu.org/licenses/>.
18 #
20 package net.kryshen.charamega
22 import java.util.Collection
23 import java.util.List
24 import java.util.ArrayList
25 import java.util.Collections
27 class Game
29 def initialize
30 @symbols = ArrayList.new
32 add_range 0x03B1, 0x03C9 # greek
33 add_all [0x0439, 0x044A, 0x0463, 0x0467] # cyrillic
34 add_range 0x20A0, 0x20B5 # currency
35 add_all [0x2190, 0x2194, 0x21CC] # arrows
36 add_all [0x221A, 0x221E, 0x222B, 0x222E] # math
37 add_all [0x2318, 0x23CE, 0x23CF, 0x23E3]
38 add_all [0x25A8, 0x25A9]
39 add_range 0x25C6, 0x25D0
40 add_all [0x25D4]
41 add_range 0x2600, 0x261A
42 add_range 0x2620, 0x2630
43 add_range 0x2638, 0x2672
44 add_range 0x267A, 0x2689
45 add_range 0x2690, 0x269C
46 add_all [0x26A0, 0x26A1]
47 add_all [0x2706, 0x2707, 0x2708, 0x2709]
48 add_all [0x270C, 0x270D, 0x270E]
49 add_all [0x2744]
51 self.players = 1
52 end
54 def shuffle
55 return false if @shuffled
56 shuffle @cards.size / 2
58 true
59 end
61 def shuffle(npairs:int):void
62 Collections.shuffle @symbols
64 cards = ArrayList.new npairs
65 time = System.nanoTime
67 @symbols.subList(0, npairs).each do |c|
68 cards.add Card.new Character(c).charValue, time
69 cards.add Card.new Character(c).charValue, time
70 end
72 Collections.shuffle cards
73 @cards = cards
74 @layout = List(nil)
75 @first = @second = Card(nil)
76 @non_matching = 0
77 @seconds = 0
78 @shuffled = true
80 compute_limits
81 end
83 def players=(nplayers:int):void
84 @matches = int[nplayers]
85 @player = 0
86 end
88 def cards
89 @cards
90 end
92 def start:void
93 shuffle
94 @shuffled = false
96 @matches.length.times { |i| @matches[i] = 0 }
97 @player = 0
99 @start_time = System.nanoTime
100 @playing = true
101 end
103 def stop:void
104 @playing = false
106 unless @shuffled
107 time = System.nanoTime
108 delay = long(2E8 / @cards.size)
110 @layout.each do |row|
111 List(row).each do |e|
112 card = Card(e)
113 card.open time unless card.matched?
114 time += delay
115 end
116 end
117 end
118 end
120 # Returns list of lists of cards for each row.
121 def layout(w:int, h:int):List
122 return @layout unless @layout.nil? or @shuffled
124 n = @cards.size
125 cols = columns(float(w) / h)
126 rows = int(Math.ceil float(n) / cols)
128 if !@layout.nil? and
129 @layout_cols == cols and
130 @layout_rows == rows
132 return @layout
133 end
135 layout = ArrayList.new rows
137 d = cols * rows - n
138 c = 0
139 rows.times do |i|
140 k = cols
141 k -= 1 if i >= rows - d
143 row = ArrayList.new k
144 k.times do
145 row.add @cards.get(c)
146 c += 1
147 end
149 layout.add row
150 end
152 Collections.shuffle layout
154 @layout_cols = cols
155 @layout_rows = rows
156 @layout = layout
157 end
159 # Called periodically by UI.
160 def update
161 time = System.nanoTime
162 @seconds = int((time - @start_time) / 1E9)
164 # Flip the cards back after some time.
165 if !@second.nil? and
166 @first.auto_close?(time) and @second.auto_close?(time)
168 @first.close time
169 @second.close time
170 return true
171 end
173 false
174 end
176 # Number of removed pairs.
177 def matched
178 matched = 0
179 @matches.each { |i| matched += i }
181 matched
182 end
184 def finished?
185 ignore_limits = false
186 # ignore_limits = true
188 matched * 2 == @cards.size or
189 (!multiplayer? and !ignore_limits and
190 (@non_matching > @max_non_matching or
191 @seconds >= @max_seconds))
192 end
194 def winner
195 max = -1
196 winner = -1
198 @matches.length.times do |i|
199 if @matches[i] > max
200 max = @matches[i]
201 winner = i
202 elsif @matches[i] == max
203 winner = -1
204 end
205 end
207 winner
208 end
210 def multiplayer?
211 @matches.length > 1
212 end
214 def status
215 if multiplayer?
216 sb = StringBuffer.new
217 sb.append 'Scores: '
218 @matches.length.times do |i|
219 sb.append ', ' if i > 0
220 sb.append '(' if i == @player
221 sb.append String.valueOf(@matches[i])
222 sb.append ')' if i == @player
223 end
224 sb.append ". "
226 if finished?
227 w = winner
228 sb.append "Player #{w + 1} wins!" if w >= 0
229 sb.append "Tie!" if w < 0
230 else
231 sb.append "Player #{@player + 1}'s turn."
232 end
234 sb.toString
235 else
236 sec = @max_seconds - @seconds
237 time = Integer[2]
238 time[0] = Integer.valueOf(sec / 60)
239 time[1] = Integer.valueOf(sec % 60)
241 sb = StringBuffer.new
242 sb.append String.format("Time left: %02d:%02d.", time)
243 sb.append " Non-matching: #{@non_matching}"
244 sb.append " / #{@max_non_matching}."
246 left = @cards.size / 2 - matched
248 if left == 0
249 sb.append " You win!"
250 puts "#{@cards.size / 2},#{@non_matching},#{@seconds}"
251 else
252 sb.append " Pairs left: #{left}."
253 end
255 sb.toString
256 end
257 end
259 def open(card:Card):boolean
260 return false if finished?
261 return false if card.matched?
262 return false if card.opened? and (@first.nil? or @second.nil?)
264 time = System.nanoTime
266 if @first.nil?
267 @first = card.open time
268 elsif @second.nil?
269 @second = card.open time
270 else
271 @first.close time
272 @second.close time
273 @first = card.open time
274 @second = nil
275 end
277 unless @second.nil?
278 if @first.symbol == @second.symbol
279 @first.match time
280 @second.match time
281 @first = @second = nil
282 @matches[@player] += 1
283 else
284 @non_matching += 1
285 @player = (@player + 1) % @matches.length
286 end
287 end
289 true
290 end
292 def hit(x:int, y:int, w:int, h:int)
293 return nil if x < 0 or y < 0
295 layout = layout(w, h)
296 i = y * layout.size / h
298 return nil if i >= layout.size
300 row = List(layout.get i)
301 j = x * row.size / w
303 return nil if j >= row.size
305 Card(row.get j)
306 end
308 private
310 def compute_limits
311 pairs = @cards.size / 2
313 @max_non_matching = 0
314 @max_seconds = 2
316 # No need to optimize this.
317 pairs.downto(3) do |i|
318 if i > 180
319 @max_non_matching += 9
320 elsif i > 150
321 @max_non_matching += 8
322 elsif i > 120
323 @max_non_matching += 7
324 elsif i > 90
325 @max_non_matching += 6
326 elsif i > 60
327 @max_non_matching += 5
328 elsif i > 30
329 @max_non_matching += 4
330 elsif i > 20
331 @max_non_matching += 3
332 elsif i > 8
333 @max_non_matching += 2
334 else
335 @max_non_matching += 1
336 end
338 if i > 160
339 @max_seconds += 30
340 elsif i > 120
341 @max_seconds += 25
342 elsif i > 80
343 @max_seconds += 20
344 elsif i > 60
345 @max_seconds += 15
346 elsif i > 40
347 @max_seconds += 12
348 elsif i > 30
349 @max_seconds += 10
350 elsif i > 20
351 @max_seconds += 8
352 elsif i > 9
353 @max_seconds += 6
354 elsif i > 5
355 @max_seconds += 4
356 else
357 @max_seconds += 2
358 end
359 end
360 end
362 def columns(aspect:float)
363 n = @cards.size
364 c = Math.max(1, int(Math.ceil Math.sqrt(n * aspect)))
366 loop do
367 m = n % c
368 break if m == 0 or c - m <= n / c
369 c -= 1
370 end
372 c
373 end
375 def add_range(from:int, to:int):void
376 from.upto(to) do |i|
377 @symbols.add Character.new char(i)
378 end
379 end
381 def add_all(xs:Collection)
382 xs.each do |i|
383 @symbols.add Character.new char(Number(i).intValue)
384 end
385 end
386 end