File: HACKING

package info (click to toggle)
gameclock 4.0-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 196 kB
  • sloc: python: 644; makefile: 4
file content (182 lines) | stat: -rw-r--r-- 7,291 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
This file documents the developpment process and the internal
structure of the code from a more general standpoint.

Development process
===================

The code is written in Python with the GTK GUI frontend. However, it
is designed to support multiple backends and a class could be written
for a ncurses frontend for example. I also thought of writing a
Gozerbot[1] plugin.

Code is maintained on Koumbit's Git repository[2]. There is a seperate
branch for the Debian package named 'debian' that tracks the
modifications to the debian/ directory, which is not packaged with the
regular distribution, as advised by various people in the debian
project[3]. So to checkout the 'upstream' code, you should use:

git clone git://git.koumbit.net/gameclock/

To checkout the Debian package version, use:

git clone -b debian http://hg.koumbit.net/gameclock/

Releases are tagged as appropriate. For version X.Y, there's a tag
named vX.Y. Debian versions have an additionnal dash (as for all
non-native packages) but otherwise follow the same naming convention.

Porters and packagers should start from tagged version and create a
named branch for their changes, if they modify the code tree
itself. For example, the above 'debian' branch was created like this:

git clone http://hg.koumbit.net/gameclock/
cd gameclock ; hg branch debian
dh_make # make the debian package
git commit -a

Building the debian package should be a breeze. Just use
git-buildpackage. I try to keep the release tags in order so that this
"just works".

Patches and bundles are of course welcome.

[1] http://gozerbot.org/
[2] https://redmine.koumbit.net/projects/gameclock/
[3] http://people.debian.org/~codehelp/#sponsor
http://people.debian.org/~mpalmer/debian-mentors_FAQ.html#packaging
http://lists.debian.org/debian-devel/2007/12/msg00630.html

Code structure
==============

General
-------

Most classes override the __str__() handler to provide a
human-readable debugging version of the object. There are some global
module variables, more or less self-documented. Command line parsing
is done in the traditionnal 'main block' of python programs.

Game class
----------

The game class is a general representation of a chess game that
doesn't take account the moves, position or board in any way. It only
counts turns and timing. It is now designed to support an arbitrary
number of players (above 0 of course).

It's mostly a placeholder for the clocks (a linked list of Clock
objects, with the head being Game.first_clock). It has a notion of the
clock current active (Game.cur_clock) that gets updated when the turns
end (Game.end_turn(), which in turns calls Clock.stop(), and
Clock.start() on the relevant clocks and increments the turn count)
and when the clocks are switched (Game.switch_clock()).

There's also a handler to start the game (Game.start()) that starts
and updates the right clock and pause the game.

The turn counting is a bit peculiar and so needs a bit of
explaining. It is counted using a float (Game.turns) that is
incremented by a half (0.5) at every Game.end_turn(). So turns, in a
way, count the number of 'two turns', which is a bit of a vocabulary
problem here.

Clock class
-----------

The clock class represents a player's clock. It can be paused, stopped
and started. The difference between pause() and stop() is that pause()
will restart the clock when called twice. stop() will also add extra
time to the player's clock if the clock's mode is fisher.

The way time is counted is this: there is an integer (Clock.time) that
counts the number of miliseconds passed playing by a player. That
number is incremented at the end of that player's turn (and also when
the game is paused). To evaluate the time spent in a turn, a float
(Clock.last_start) that marks the beginning of the turn (or the last
pause()) is marked when the turn starts (or when the game is
unpaused). When the time ends (or the game is pause()d), that time is
compared to the current time as returned by by Python's time()[4]
function and is added to the player's clock time (Clock.time).

All that processing is isolated in Clock.get_time().

So in summary Clock.time contains the time spent by the player
throughout the game not including the turn he's currently playing (if
any). Therefore, to get the proper value, Clock.get_time() needs to be
used.

The Clock class also keeps an string representation (Clock.text), a
cache of a human-readable version of the clock's state. It displays
the hours, minutes and seconds of time counted by the clock. Depending
on the truth value of Clock.miliseconds, it will also (or not) show
the miliseconds. If the clock goes beyond 24h, it will also display
the number of days. If the clock goes below zero it will display a
minus in front of the string.

For performance reasons, that cache is updated only when relevant
(with Clock.update()). Care has been taken to call that function as
little as possible.

Python's strftime() function[4] is currently used for rendering hours,
minutes and seconds.

Similarly, the clock keeps a cache of the negativness of the clock
(Clock.dead). That cache is also updated only when necessary (again
with Clock.update()).

The clock depends on the Game to manage clock changes and its internal
engine is therefore considered to be exposed to other classes.

Also note that a Clock is usually part of a chained list of clocks
through the Clock.next pointer.

FisherClock class
'''''''''''''''''

This is a subclass of the generic Clock class that implements fisher
style time tracking: the stop() function has simply been overriden
to add n miliseconds to the clock before switching.

Also note that the constructor is different to allow customization of
that delay.

Clock precision
'''''''''''''''

There are some areas where the clock might introduce some
imprecision. It can be due to Python's floating point arithmetics,
since the number of miliseconds is deduced from the mantissa of the
float returned by time(), but that's probably negligible.

Most of the imprecision is likely to come from the time spent in the
end_turn() function (and of course the general system processing
between the players brain's, the computer keyboard, the kernel, X11,
GTK and the Python main loop).

I would expect this to be lower than 10ms, but I have absolutely no
metrics to prove that assertion.

[4] http://docs.python.org/lib/module-time.html

User interface
--------------

When/if a new frontend is written, it would probably appropriate to
refactor some code of the GameclockGtkUI class into a parent class. In
the meantime, that code was moved to a separate file to ease
extensibility.

The GTK UI code has became messy. Some work has been done to uncouple
it from the game engine, but it still needs to be improved on that
side. A significant amount of work was done to move the UI buttons to
a separate window that pops up on start up and doesn't clutter the
UI. The code is not much more readable but at least there is more
isolation between the game handling and configuration sides.

There is a ClockUI subclass that regroups each clock's widget. As the
Clock class, it is organised as a chained list and can be iterated
similarly.

A next step would be to cleanup the gtkui.py file to make it more
readable and modular.