view src/net/kryshen/charamega/game.mirah @ 15:8ed3a7b4f6e9

Changed homepage URL.
author Mikhail Kryshen <mikhail@kryshen.net>
date Sun, 26 Jan 2014 16:12:22 +0400
parents 6e6b00d95d0b
children
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
26 import java.util.Properties
28 class Game
30 def initialize
31 @symbols = ArrayList.new
33 add_range 0x03B1, 0x03C9 # greek
34 add_all [0x0439, 0x044A, 0x0463, 0x0467] # cyrillic
35 add_range 0x20A0, 0x20B5 # currency
36 add_all [0x2190, 0x2194, 0x21CC] # arrows
37 add_all [0x221A, 0x221E, 0x222B, 0x222E] # math
38 add_all [0x2318, 0x23CE, 0x23CF, 0x23E3]
39 add_all [0x25A8, 0x25A9]
40 add_range 0x25C6, 0x25D0
41 add_all [0x25D4]
42 add_range 0x2600, 0x261A
43 add_range 0x2620, 0x2630
44 add_range 0x2638, 0x2672
45 add_range 0x267A, 0x2689
46 add_range 0x2690, 0x269C
47 add_all [0x26A0, 0x26A1]
48 add_all [0x2706, 0x2707, 0x2708, 0x2709]
49 add_all [0x270C, 0x270D, 0x270E]
50 add_all [0x2744]
52 self.players = 1
53 end
55 def ensure_shuffled
56 return false if @shuffled
57 shuffle @cards.size / 2
59 true
60 end
62 def shuffle(npairs:int):void
63 Collections.shuffle @symbols
65 cards = ArrayList.new npairs
66 time = System.nanoTime
68 @symbols.subList(0, npairs).each do |c|
69 cards.add Card.new Character(c).charValue, time
70 cards.add Card.new Character(c).charValue, time
71 end
73 Collections.shuffle cards
74 @cards = cards
75 @layout = List(nil)
76 @first = @second = Card(nil)
77 @non_matching = 0
78 @seconds = 0
79 @shuffled = true
81 compute_limits
82 end
84 def players=(nplayers:int):void
85 @matches = int[nplayers]
86 @player = 0
87 end
89 def cards
90 @cards
91 end
93 def start:void
94 ensure_shuffled
95 @shuffled = false
97 @matches.length.times { |i| @matches[i] = 0 }
98 @player = 0
100 @start_time = System.nanoTime
101 @playing = true
102 end
104 def stop:void
105 @playing = false
107 unless @shuffled
108 time = System.nanoTime
109 delay = long(2E8 / @cards.size)
111 @layout.each do |row|
112 List(row).each do |e|
113 card = Card(e)
114 card.open time unless card.matched?
115 time += delay
116 end
117 end
118 end
119 end
121 # Returns list of lists of cards for each row.
122 def layout(w:int, h:int):List
123 return @layout unless @layout.nil? or @shuffled
125 n = @cards.size
126 cols = columns(float(w) / h)
127 rows = int(Math.ceil float(n) / cols)
129 if !@layout.nil? and
130 @layout_cols == cols and
131 @layout_rows == rows
133 return @layout
134 end
136 layout = ArrayList.new rows
138 d = cols * rows - n
139 c = 0
140 rows.times do |i|
141 k = cols
142 k -= 1 if i >= rows - d
144 row = ArrayList.new k
145 k.times do
146 row.add @cards.get(c)
147 c += 1
148 end
150 layout.add row
151 end
153 Collections.shuffle layout
155 @layout_cols = cols
156 @layout_rows = rows
157 @layout = layout
158 end
160 # Called periodically by UI.
161 def update
162 time = System.nanoTime
163 @seconds = int((time - @start_time) / 1E9)
165 # Flip the cards back after some time.
166 if !@second.nil? and
167 @first.auto_close?(time) and @second.auto_close?(time)
169 @first.close time
170 @second.close time
171 return true
172 end
174 false
175 end
177 # Number of removed pairs.
178 def matched
179 matched = 0
180 @matches.each { |i| matched += i }
182 matched
183 end
185 def finished?
186 ignore_limits = false
187 # ignore_limits = true
189 matched * 2 == @cards.size or
190 (!multiplayer? and !ignore_limits and
191 (@non_matching > @max_non_matching or
192 @seconds >= @max_seconds))
193 end
195 def winner
196 max = -1
197 winner = -1
199 @matches.length.times do |i|
200 if @matches[i] > max
201 max = @matches[i]
202 winner = i
203 elsif @matches[i] == max
204 winner = -1
205 end
206 end
208 winner
209 end
211 def multiplayer?
212 @matches.length > 1
213 end
215 def status
216 if multiplayer?
217 sb = StringBuffer.new
218 sb.append 'Scores: '
219 @matches.length.times do |i|
220 sb.append ', ' if i > 0
221 sb.append '(' if @playing and i == @player
222 sb.append String.valueOf(@matches[i])
223 sb.append ')' if @playing and i == @player
224 end
225 sb.append ". "
227 if finished?
228 w = winner
229 sb.append "Player #{w + 1} wins!" if w >= 0
230 sb.append "Tie!" if w < 0
231 else
232 sb.append "Player #{@player + 1}'s turn."
233 end
235 sb.toString
236 else
237 sec = @max_seconds - @seconds
238 time = Integer[2]
239 time[0] = Integer.valueOf(sec / 60)
240 time[1] = Integer.valueOf(sec % 60)
242 sb = StringBuffer.new
243 sb.append String.format("Time left: %02d:%02d.", time)
244 sb.append " Non-matching: #{@non_matching}"
245 sb.append " / #{@max_non_matching}."
247 left = @cards.size / 2 - matched
249 if left == 0
250 sb.append " You win!"
251 puts "#{@cards.size / 2},#{@non_matching},#{@seconds}"
252 else
253 sb.append " Pairs left: #{left}."
254 end
256 sb.toString
257 end
258 end
260 def open(card:Card):boolean
261 return false if finished?
262 return false if card.matched?
263 return false if card.opened? and (@first.nil? or @second.nil?)
265 time = System.nanoTime
267 if @first.nil?
268 @first = card.open time
269 elsif @second.nil?
270 @second = card.open time
271 else
272 @first.close time
273 @second.close time
274 @first = card.open time
275 @second = nil
276 end
278 unless @second.nil?
279 if @first.symbol == @second.symbol
280 @first.match time
281 @second.match time
282 @first = @second = nil
283 @matches[@player] += 1
284 else
285 @non_matching += 1
286 @player = (@player + 1) % @matches.length
287 end
288 end
290 true
291 end
293 def hit(x:int, y:int, w:int, h:int):Card
294 return nil if x < 0 or y < 0
296 layout = layout(w, h)
297 i = y * layout.size / h
299 return nil if i >= layout.size
301 row = List(layout.get i)
302 j = x * row.size / w
304 return nil if j >= row.size
306 Card(row.get j)
307 end
309 def version
310 return @version unless @version.nil?
312 begin
313 source = getClass.getResourceAsStream("version.properties")
314 properties = Properties.new.load(source)
315 ensure
316 source.close unless source.nil?
317 end
319 @version = properties.getProperty("version")
320 end
322 #private
324 def compute_limits
325 pairs = @cards.size / 2
327 @max_non_matching = 0
328 @max_seconds = 2
330 # No need to optimize this.
331 pairs.downto(3) do |i|
332 if i > 180
333 @max_non_matching += 9
334 elsif i > 150
335 @max_non_matching += 8
336 elsif i > 120
337 @max_non_matching += 7
338 elsif i > 90
339 @max_non_matching += 6
340 elsif i > 60
341 @max_non_matching += 5
342 elsif i > 30
343 @max_non_matching += 4
344 elsif i > 20
345 @max_non_matching += 3
346 elsif i > 8
347 @max_non_matching += 2
348 else
349 @max_non_matching += 1
350 end
352 if i > 160
353 @max_seconds += 30
354 elsif i > 120
355 @max_seconds += 25
356 elsif i > 80
357 @max_seconds += 20
358 elsif i > 60
359 @max_seconds += 15
360 elsif i > 40
361 @max_seconds += 12
362 elsif i > 30
363 @max_seconds += 10
364 elsif i > 20
365 @max_seconds += 8
366 elsif i > 9
367 @max_seconds += 6
368 elsif i > 5
369 @max_seconds += 4
370 else
371 @max_seconds += 2
372 end
373 end
374 end
376 def columns(aspect:float)
377 n = @cards.size
378 c = Math.max(1, int(Math.ceil Math.sqrt(n * aspect)))
380 loop do
381 m = n % c
382 break if m == 0 or c - m <= n / c
383 c -= 1
384 end
386 c
387 end
389 def add_range(from:int, to:int):void
390 from.upto(to) do |i|
391 @symbols.add Character.new char(i)
392 end
393 end
395 def add_all(xs:Collection)
396 xs.each do |i|
397 @symbols.add Character.new char(Number(i).intValue)
398 end
399 end
400 end