Mercurial > hg > charamega
view src/net/kryshen/charamega/game.mirah @ 3:1bbcb3d4302f
Updated .hgignore.
author | Mikhail Kryshen <mikhail@kryshen.net> |
---|---|
date | Sat, 14 Jul 2012 06:55:35 +0400 |
parents | 91ecd24948de |
children | 28c25c08059d |
line wrap: on
line source
# # Copyright 2012 Mikhail Kryshen <mikhail@kryshen.net> # # This file is part of Charamega. # # Charamega is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Charamega is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Charamega. If not, see <http://www.gnu.org/licenses/>. # package net.kryshen.charamega import java.util.Collection import java.util.List import java.util.ArrayList import java.util.Collections class Game def initialize @symbols = ArrayList.new add_range 0x03B1, 0x03C9 # greek add_all [0x0439, 0x044A, 0x0463, 0x0467] # cyrillic add_range 0x20A0, 0x20B5 # currency add_all [0x2190, 0x2194, 0x21CC] # arrows add_all [0x221A, 0x221E, 0x222B, 0x222E] # math add_all [0x2318, 0x23CE, 0x23CF, 0x23E3] add_all [0x25A8, 0x25A9] add_range 0x25C6, 0x25D0 add_all [0x25D4] add_range 0x2600, 0x261A add_range 0x2620, 0x2630 add_range 0x2638, 0x2672 add_range 0x267A, 0x2689 add_range 0x2690, 0x269C add_all [0x26A0, 0x26A1] add_all [0x2706, 0x2707, 0x2708, 0x2709] add_all [0x270C, 0x270D, 0x270E] add_all [0x2744] self.players = 1 end def shuffle return false if @shuffled shuffle @cards.size / 2 true end def shuffle(npairs:int):void Collections.shuffle @symbols cards = ArrayList.new npairs time = System.nanoTime @symbols.subList(0, npairs).each do |c| cards.add Card.new Character(c).charValue, time cards.add Card.new Character(c).charValue, time end Collections.shuffle cards @cards = cards @layout = List(nil) @first = @second = Card(nil) @non_matching = 0 @seconds = 0 @shuffled = true compute_limits end def players=(nplayers:int):void @matches = int[nplayers] @player = 0 end def cards @cards end def start:void shuffle @shuffled = false @matches.length.times { |i| @matches[i] = 0 } @player = 0 @start_time = System.nanoTime @playing = true end def stop:void @playing = false unless @shuffled time = System.nanoTime delay = long(2E8 / @cards.size) @layout.each do |row| List(row).each do |e| card = Card(e) card.open time unless card.matched? time += delay end end end end # Returns list of lists of cards for each row. def layout(w:int, h:int):List return @layout unless @layout.nil? or @shuffled n = @cards.size cols = columns(float(w) / h) rows = int(Math.ceil float(n) / cols) if !@layout.nil? and @layout_cols == cols and @layout_rows == rows return @layout end layout = ArrayList.new rows d = cols * rows - n c = 0 rows.times do |i| k = cols k -= 1 if i >= rows - d row = ArrayList.new k k.times do row.add @cards.get(c) c += 1 end layout.add row end Collections.shuffle layout @layout_cols = cols @layout_rows = rows @layout = layout end # Called periodically by UI. def update time = System.nanoTime @seconds = int((time - @start_time) / 1E9) # Flip the cards back after some time. if !@second.nil? and @first.auto_close?(time) and @second.auto_close?(time) @first.close time @second.close time return true end false end # Number of removed pairs. def matched matched = 0 @matches.each { |i| matched += i } matched end def finished? ignore_limits = false # ignore_limits = true matched * 2 == @cards.size or (!multiplayer? and !ignore_limits and (@non_matching > @max_non_matching or @seconds >= @max_seconds)) end def winner max = -1 winner = -1 @matches.length.times do |i| if @matches[i] > max max = @matches[i] winner = i elsif @matches[i] == max winner = -1 end end winner end def multiplayer? @matches.length > 1 end def status if multiplayer? sb = StringBuffer.new sb.append 'Scores: ' @matches.length.times do |i| sb.append ', ' if i > 0 sb.append '(' if i == @player sb.append String.valueOf(@matches[i]) sb.append ')' if i == @player end sb.append ". " if finished? w = winner sb.append "Player #{w + 1} wins!" if w >= 0 sb.append "Tie!" if w < 0 else sb.append "Player #{@player + 1}'s turn." end sb.toString else sec = @max_seconds - @seconds time = Integer[2] time[0] = Integer.valueOf(sec / 60) time[1] = Integer.valueOf(sec % 60) sb = StringBuffer.new sb.append String.format("Time left: %02d:%02d.", time) sb.append " Non-matching: #{@non_matching}" sb.append " / #{@max_non_matching}." left = @cards.size / 2 - matched if left == 0 sb.append " You win!" puts "#{@cards.size / 2},#{@non_matching},#{@seconds}" else sb.append " Pars left: #{left}." end sb.toString end end def open(card:Card):boolean return false if finished? return false if card.matched? return false if card.opened? and (@first.nil? or @second.nil?) time = System.nanoTime if @first.nil? @first = card.open time elsif @second.nil? @second = card.open time else @first.close time @second.close time @first = card.open time @second = nil end unless @second.nil? if @first.symbol == @second.symbol @first.match time @second.match time @first = @second = nil @matches[@player] += 1 else @non_matching += 1 @player = (@player + 1) % @matches.length end end true end def hit(x:int, y:int, w:int, h:int) return nil if x < 0 or y < 0 layout = layout(w, h) i = y * layout.size / h return nil if i >= layout.size row = List(layout.get i) j = x * row.size / w return nil if j >= row.size Card(row.get j) end private def compute_limits pairs = @cards.size / 2 @max_non_matching = 0 @max_seconds = 2 # No need to optimize this. pairs.downto(3) do |i| if i > 180 @max_non_matching += 9 elsif i > 150 @max_non_matching += 8 elsif i > 120 @max_non_matching += 7 elsif i > 90 @max_non_matching += 6 elsif i > 60 @max_non_matching += 5 elsif i > 30 @max_non_matching += 4 elsif i > 20 @max_non_matching += 3 elsif i > 8 @max_non_matching += 2 else @max_non_matching += 1 end if i > 160 @max_seconds += 30 elsif i > 120 @max_seconds += 25 elsif i > 80 @max_seconds += 20 elsif i > 60 @max_seconds += 15 elsif i > 40 @max_seconds += 12 elsif i > 20 @max_seconds += 9 elsif i > 9 @max_seconds += 6 elsif i > 5 @max_seconds += 4 else @max_seconds += 2 end end end def columns(aspect:float) n = @cards.size c = Math.max(1, int(Math.ceil Math.sqrt(n * aspect))) loop do m = n % c break if m == 0 or c - m <= n / c c -= 1 end c end def add_range(from:int, to:int):void from.upto(to) do |i| @symbols.add Character.new char(i) end end def add_all(xs:Collection) xs.each do |i| @symbols.add Character.new char(Number(i).intValue) end end end