/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * Copyright (C) 2010-2013 Robert Ancell * Copyright (C) 2013-2014 Michael Catanzaro * Copyright (C) 2015-2016 Sahil Sareen * * This program 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. See http://www.gnu.org/copyleft/gpl.html the full text of the * license. */ public enum ClockType { SIMPLE, FISCHER, BRONSTEIN, INVALID; public string to_string () { switch (this) { case SIMPLE: return "simple"; case FISCHER: return "fischer"; case BRONSTEIN: return "bronstein"; default: assert_not_reached (); } } public static ClockType string_to_enum (string s) { switch (s) { case "simple": return SIMPLE; case "fischer": return FISCHER; case "bronstein": return BRONSTEIN; default: return INVALID; } } } public class ChessClock : Object { private int white_initial_seconds; private int black_initial_seconds; private int white_seconds_used = 0; private int black_seconds_used = 0; private int white_prev_move_seconds = 0; private int black_prev_move_seconds = 0; private int white_extra_seconds = 0; private int black_extra_seconds = 0; public int extra_seconds { get; set; default = 0; } public int white_remaining_seconds { get { return white_initial_seconds + white_extra_seconds - white_seconds_used; } } public int black_remaining_seconds { get { return black_initial_seconds + black_extra_seconds - black_seconds_used; } } public ClockType clock_type { get; set; default = ClockType.SIMPLE; } private Color _active_color = Color.WHITE; public Color active_color { get { return _active_color; } set { if (value == active_color) return; stop (); _active_color = value; // This is a move switch // Update the clocks for Fischer and Bronstein mode update_extra_seconds (); update_prev_move_time (); start (); } } private Timer? timer; private uint tick_timeout_id = 0; public signal void tick (); public signal void expired (); private bool is_active = false; public ChessClock (int white_initial_seconds, int black_initial_seconds) { this.white_initial_seconds = white_initial_seconds; this.black_initial_seconds = black_initial_seconds; } public void start () { if (is_active) return; is_active = true; if (timer == null) { /* Starts automatically */ timer = new Timer (); } else { timer.start (); } watch_timer (); } private bool tick_cb () { if (active_color == Color.WHITE) white_seconds_used++; else black_seconds_used++; tick (); if (white_seconds_used >= white_initial_seconds + white_extra_seconds || black_seconds_used >= black_initial_seconds + black_extra_seconds) { stop (); expired (); } return Source.CONTINUE; } public void stop () { if (!is_active) return; timer.stop (); stop_watching_timer (); is_active = false; } public void pause () { if (!is_active) return; timer.stop (); stop_watching_timer (); is_active = false; } public void unpause () { if (timer == null || is_active) return; timer.@continue (); watch_timer (); is_active = true; } private void watch_timer () { /* Wake up each second */ tick_timeout_id = Timeout.add (1000, tick_cb); } private void stop_watching_timer () { Source.remove (tick_timeout_id); tick_timeout_id = 0; } public void update_prev_move_time () { if (active_color == Color.WHITE) black_prev_move_seconds = black_seconds_used; else white_prev_move_seconds = white_seconds_used; } private void update_extra_seconds () { int white_move_used = 0, black_move_used = 0; switch (clock_type) { case ClockType.FISCHER: if (active_color == Color.WHITE) white_extra_seconds += extra_seconds; else black_extra_seconds += extra_seconds; break; case ClockType.BRONSTEIN: white_move_used = white_seconds_used - white_prev_move_seconds; black_move_used = black_seconds_used - black_prev_move_seconds; if (active_color != Color.WHITE) white_extra_seconds += int.min (extra_seconds, white_move_used); else black_extra_seconds += int.min (extra_seconds, black_move_used); break; default: break; } } }