@@ -47,23 +47,13 @@ def fromfile(cls, file, *, nrows=25, ncols=25):
4747
4848 field = field [:- 1 ]
4949
50- line = file .readline (5 )
51- if line .endswith ('\n ' ):
52- line = line [:- 1 ]
50+ points = _readint (file , 7 , cls .NUMBER_PATTERN , 1 , 1000000 , nrows + 1 , newline = True )
51+ max_bytes = _readint (file , 4 , cls .NUMBER_PATTERN , 1 , 1000 , nrows + 2 )
5352
54- match = cls .NUMBER_PATTERN .fullmatch (line )
55- error = ValueError ('expected a positive integer in the range [1, 1000) at line %d: %s' % (nrows + 1 , line ))
53+ return cls (field , points , max_bytes , nrows , ncols )
5654
57- if match :
58- max_bytes = int (line )
59- if max_bytes < 1 or max_bytes >= 1000 :
60- raise error
61- else :
62- raise error
63-
64- return cls (field , max_bytes , nrows , ncols )
65-
66- def __init__ (self , field , max_bytes , nrows , ncols ):
55+ def __init__ (self , field , points , max_bytes , nrows , ncols ):
56+ self .points = points
6757 self .max_bytes = max_bytes
6858 self .nrows = nrows
6959 self .ncols = ncols
@@ -154,6 +144,28 @@ def _parse(self, field):
154144 self .inaccessible_spots = inaccessible_spots
155145
156146
147+ def _readint (file , size , pattern , lo , hi , row , newline = False ):
148+ line = file .readline (size )
149+ error = ValueError ('expected a positive integer in the range [%d, %d) at line %d: %s' % (lo , hi , row , line ))
150+
151+ if line .endswith ('\n ' ):
152+ line = line [:- 1 ]
153+ elif newline :
154+ raise error
155+
156+ match = pattern .fullmatch (line )
157+
158+ if match :
159+ result = int (line )
160+
161+ if result < lo or result >= hi :
162+ raise error
163+
164+ return result
165+
166+ raise error
167+
168+
157169def _extend_horizontally (grid , hseen , row , col , nrows , ncols ):
158170 len = 0
159171 hseen .add ((row , col ))
@@ -270,6 +282,7 @@ def __init__(self, level, robot, gray_buttons, white_buttons):
270282 self .robot = robot
271283 self .gray_buttons = gray_buttons
272284 self .white_buttons = white_buttons
285+ self .total_buttons = len (white_buttons )
273286 self .npressed = 0 # the number of white buttons pressed
274287 self .max_npressed = 0 # the maximum number of white buttons pressed
275288 self .completed = False # True iff all the white buttons have been pressed
@@ -290,11 +303,42 @@ def step(self, command):
290303 if self .npressed > self .max_npressed :
291304 self .max_npressed = self .npressed
292305
293- if not self .completed and self .white_buttons and self .npressed == len ( self .white_buttons ) :
306+ if not self .completed and self .white_buttons and self .npressed == self .total_buttons :
294307 self .completed = True
295308 elif command == 'l' :
296309 self .robot .turn_left ()
297310 elif command == 'r' :
298311 self .robot .turn_right ()
299312 else :
300313 raise ValueError ('not a command: %s' % command )
314+
315+ def score (self , bytes ):
316+ return calculate_score (self .level .points , self .level .max_bytes , self .total_buttons , self .npressed , bytes )
317+
318+
319+ def calculate_score (points , max_bytes , total_buttons , buttons , bytes ):
320+ """Calculates the score for a level.
321+
322+ points: the points assigned for solving the level (based on difficulty)
323+ max_bytes: the maximum number of bytes for the level
324+ total_buttons: the number of white buttons on the level
325+ buttons: the number of white buttons pressed
326+ bytes: the number of bytes actually used
327+ """
328+
329+ if buttons == total_buttons and bytes <= max_bytes :
330+ # level solved
331+ return (points * max_bytes ) // bytes
332+
333+ # level unsolved
334+ assert buttons < total_buttons or bytes > max_bytes
335+
336+ points_per_button = 0
337+
338+ if bytes <= max_bytes :
339+ points_per_button = points // (2 * total_buttons )
340+
341+ if max_bytes < bytes <= 2 * max_bytes :
342+ points_per_button = points * (2 * max_bytes - bytes ) // (2 * max_bytes * total_buttons )
343+
344+ return buttons * points_per_button
0 commit comments