forked from ewindisch/pydance
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathjudge.py
147 lines (125 loc) · 4.87 KB
/
judge.py
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
from constants import *
from util import toRealTime
from announcer import Announcer
from listener import Listener
# The judge is responsible for correlating step times and arrows times,
# and then rating them (V/P/G/O/B), as well as expiring arrows when
# they're missed. Listener objects get their information from the judge,
# which is also a Listener itself.
class AbstractJudge(Listener):
def __init__ (self, pid, songconf):
self._pid = pid
self._scale = songconf["judgescale"]
self._ann = Announcer(mainconfig["djtheme"])
def set_song(self, pid, bpm, difficulty, count, holds, feet):
self.holdsub = {}
self._steps = {}
# Hidden steps were first used in Technomotion. They count for points,
# if you hit them, but you can't miss them.
self._hidden_steps = {}
def broke_hold(self, pid, curtime, dir, whichone):
if pid != self._pid: return
if self.holdsub.get(whichone) != -1: self.holdsub[whichone] = -1
# Handle a key press and see if it can be associated with an upcoming
# arrow; rate it if so.
def handle_key(self, dir, curtime):
times = self._steps.keys()
times.sort()
etime = 0.0
done = 0
rating = None
off = -1
for t in times:
if dir in self._steps[t]:
rating = self._get_rating(curtime, t)
if rating != None:
etime = t
self._steps[etime] = self._steps[etime].replace(dir, "")
if etime in self._hidden_steps:
self._hidden_steps[etime].replace(dir, "")
break
return rating, dir, etime
# Add an arrow to the list of steps to be checked on a keypress.
def handle_arrow(self, key, etime, is_hidden):
if etime in self._steps: self._steps[etime] += key
else: self._steps[etime] = key
if is_hidden:
if etime in self._hidden_steps: self._hidden_steps[etime] += key
else: self._hidden_steps[etime] = key
# Mark arrows that are very old as misses.
def expire_arrows(self, curtime):
times = self._steps.keys()
misses = ""
for time in times:
if self._is_miss(curtime, time) and self._steps[time]:
for d in self._hidden_steps.get(time, ""):
self._steps[time] = self._steps[time].replace(d, "")
misses += self._steps[time]
del(self._steps[time])
return misses
# Check whether or not an arrow is a miss.
def _is_miss(self, curtime, time):
raise NotImplementedError("This class should not be instantiated.")
# Rate a (possible) step.
def _get_rating(self, curtime, time):
raise NotImplementedError("This class should not be instantiated.")
# This judge rates steps based on constant time offsets (although
# multiplied by scale).
class TimeJudge(AbstractJudge):
def __init__ (self, pid, songconf):
AbstractJudge.__init__(self, pid, songconf)
self._v = self._scale * 0.0225
self._p = self._scale * 0.045
self._g = self._scale * 0.090
self._o = self._scale * 0.135
self._b = self._scale * 0.180
self.ok_time = self._scale * 0.25
def _get_rating(self, curtime, t):
offset = abs(curtime - t)
if offset < self._v: return "V"
elif offset < self._p: return "P"
elif offset < self._g: return "G"
elif offset < self._o: return "O"
elif offset < self._b: return "B"
else: return None
def _is_miss(self, curtime, time): return time < curtime - self._b
# This judge rates steps based on what fraction of a beat they are from
# the correct time, and therefore makes fast songs much harder, and slow
# songs easier.
class BeatJudge(AbstractJudge):
def set_song(self, pid, bpm, difficulty, count, holds, feet):
AbstractJudge.set_song(self, pid, bpm, difficulty, count, holds, feet)
self._tick = toRealTime(bpm, 0.16666666666666666)
self._v = self._scale * 1
self._p = self._scale * 4
self._g = self._scale * 7
self._o = self._scale * 9
self._b = self._scale * 12
self.ok_time = toRealTime(bpm, 2) * self._scale
self._b_ticks = self._b * self._tick
def change_bpm(self, pid, curtime, bpm):
if self._pid != pid: return
if bpm >= 1: self._tick = toRealTime(bpm, 0.16666666666666666)
self._v = self._scale * 1
self._p = self._scale * 4
self._g = self._scale * 7
self._o = self._scale * 9
self._b = self._scale * 12
self.ok_time = toRealTime(bpm, 2) * self._scale
self._b_ticks = self._b * self._tick
def _get_rating(self, curtime, t):
off = abs((curtime - t) / self._tick)
if off <= self._v: return "V"
elif off <= self._p: return "P"
elif off <= self._g: return "G"
elif off <= self._o: return "O"
elif off < self._b: return "B"
else: return None
def _is_miss(self, curtime, time): return time < curtime - self._b
judges = [TimeJudge, BeatJudge]
judge_opt = [
(0, _("Time"),
_("Judging is based on how many seconds you are from the correct time.")),
(1, _("Beat"),
_("Judging is based on how many beats you are from the correct time.")),
]