@@ -267,22 +267,39 @@ def from_string(cls, string):
267267 @staticmethod
268268 def _parse (string ):
269269 tokens = string .split ()
270+ if not tokens :
271+ raise ValueError ('empty .gam input' )
272+ pos = 0
273+
274+ # N
275+ tok = tokens [pos ]
276+ try :
277+ N = int (tok )
278+ except ValueError as err :
279+ raise ValueError (f'invalid N token: { tok !r} ' ) from err
280+ pos += 1
281+
282+ if N <= 0 :
283+ raise ValueError ('N must be a positive integer' )
284+
285+ # nums_actions
286+ if len (tokens ) < pos + N :
287+ got = max (0 , len (tokens ) - pos )
288+ raise ValueError (
289+ f'incomplete header: expected { N } action counts, got { got } '
290+ )
270291
271- N = int (tokens .pop (0 ))
272- nums_actions = tuple (int (tokens .pop (0 )) for _ in range (N ))
273- payoffs = np .array ([_str2num (s ) for s in tokens ])
292+ try :
293+ nums_actions = tuple (int (tok ) for tok in tokens [pos :pos + N ])
294+ except ValueError as err :
295+ raise ValueError ('invalid action count token in header' ) from err
296+ pos += N
274297
275- na = np .prod (nums_actions )
276- payoffs2d = payoffs .reshape (N , na )
277- players = [
278- Player (
279- payoffs2d [i , :].reshape (nums_actions , order = 'F' ).transpose (
280- (* range (i , N ), * range (i ))
281- )
282- ) for i in range (N )
283- ]
298+ # payoffs
299+ payoffs = np .array ([_str2num (tok ) for tok in tokens [pos :]])
284300
285- return NormalFormGame (players )
301+ p = GAMPayoffVector (nums_actions , payoffs )
302+ return p .to_nfg ()
286303
287304
288305class GAMWriter :
@@ -376,6 +393,14 @@ def from_gam(filename: str) -> NormalFormGame:
376393 return GAMReader .from_file (filename )
377394
378395
396+ def from_gam_string (string ):
397+ return GAMReader .from_string (string )
398+
399+
400+ def from_gam_url (url ):
401+ return GAMReader .from_url (url )
402+
403+
379404def to_gam (g , file_path = None ):
380405 """
381406 Write a NormalFormGame to a file in .gam format.
0 commit comments