MSAC
[imago.git] / src / timer.py
1 #!/usr/bin/env python
2
3 """Go timer.
4
5 This module defines a UI that combines game clock with automatic game recording.
6 When player presses the clock, an image of the board is taken. The sequence of
7 images can later be analysed by Imago to produce a game record in abstract
8 notation.
9 """
10
11 import os
12 import sys
13 import time
14 import argparse
15 import subprocess
16
17 import pygame
18
19 class Timer:
20     """Keeps track of time remaining one player."""
21     def __init__(self, main_time, byop, byot):
22         self._last = 0
23         self._elapsed = 0.
24         self._main_t = main_time
25         self.byost = "main time"
26         self._byop = byop + 1
27         self._byot = byot
28         self._byo = False
29
30     def get_time(self):
31         """Return string representation of remaining time."""
32         if self._last > 0: # when running:
33             r_time = self._main_t - (time.time() - self._last + self._elapsed)
34             if (r_time - int(r_time)) < 0.75:
35                 sep = ":"
36             else:
37                sep = " "
38         else: # not running:
39             if self._byo and self._byop > 0:
40                 self._elapsed = 0
41             r_time = self._main_t - self._elapsed
42             sep = ":"
43         if r_time < 0:
44             self._byo = True
45             self._main_t = self._byot
46             self._byop -= 1
47             if self._byop > 0:
48                 self._elapsed = 0
49                 self._last = time.time()
50                 self.byost = "(" + str(self._byop) + ")"
51                 return self.get_time()
52             else:
53                 r_time = 0
54                 self.byost = "lost on time"
55         return "{0:0>2}".format(int(r_time / 60)) + sep + "{0:0>2}".format(int(r_time % 60))
56
57     def start(self):
58         """Start the clock."""
59         self._last = time.time()
60
61     def stop(self):
62         """Stop the clock."""
63         self._elapsed += time.time() - self._last
64         self._last = 0
65
66     def is_running(self):
67         """Return True if the clock is running."""
68         if self._last > 0:
69             return True
70         else:
71             return False
72
73 def main():
74     """Parse the arguments and run the clock."""
75     # TODO refactor into smaller functions
76     parser = argparse.ArgumentParser(description=__doc__)
77     parser.add_argument('-m', type=int, default=10,
78                         help="main time in minutes (default is 10)")
79     parser.add_argument('-b', type=int, nargs=2, default=[5, 20],
80                         help="japanese byoyomi: number of periods, period length in"
81                             " seconds (default is 5 periods, 20 seconds)")
82     parser.add_argument('-c', '--camera', dest='cam', action='store_true',
83                         help="camera on")
84     parser.add_argument('-d', type=int, default=0,
85                         help="video device id")
86     parser.add_argument('-r', type=int, nargs=2, default=[640, 480],
87                         help="set camera resolution")
88     args = parser.parse_args()
89
90
91     pygame.init()
92     pygame.display.set_mode((600, 130))
93     pygame.display.set_caption("Go timer")
94     screen = pygame.display.get_surface()
95
96     clock = pygame.time.Clock()
97
98     font = pygame.font.Font(pygame.font.match_font('monospace'), 80)
99     font2 = pygame.font.Font(None, 25)
100
101     done = False
102     first = True
103
104     main_time = args.m * 60
105
106     timers = (Timer(main_time, args.b[0], args.b[1]), Timer(main_time, args.b[0], args.b[1]))
107
108     if args.cam:
109         capture = subprocess.Popen(['python', 'capture.py', '-c', '-d', str(args.d), '-r',
110                           str(args.r[0]),
111                           str(args.r[1])], stdin=subprocess.PIPE)
112     last = 0
113
114     while not done:
115         for event in pygame.event.get():
116             if event.type == pygame.QUIT:
117                 done = True
118             elif event.type == pygame.KEYDOWN:
119                 if time.time() - last < 0.7:
120                     continue
121                 last = time.time()
122                 if first:
123                     timers[0].start()
124                     first = False
125                     if args.cam:
126                         print >> capture.stdin, "stop"
127                         print >> capture.stdin, "take"
128                     continue
129                 if args.cam:
130                     print >> capture.stdin, "take"
131                 for timer in timers:
132                     if timer.is_running():
133                         timer.stop()
134                     else:
135                         timer.start()
136
137         screen.fill([0, 0, 0])
138         text1 = font.render(timers[0].get_time(), True, [128, 255, 128])
139         screen.blit(text1, [10, 10])
140         text2 = font.render(timers[1].get_time(), True, [128, 255, 128])
141         screen.blit(text2, [300, 10])
142         text3 = font2.render(timers[0].byost, True, [128, 255, 128])
143         screen.blit(text3, [10, 90])
144         text4 = font2.render(timers[1].byost, True, [128, 255, 128])
145         screen.blit(text4, [300, 90])
146         pygame.display.flip()
147         clock.tick(15)
148
149     if args.cam:
150         print >> capture.stdin, "exit"
151
152 if __name__ == '__main__':
153     try:
154         main()
155     finally:
156         pygame.quit()