7
7
Create a QuantEcon NormalFormGame from a gam file storing
8
8
a 3-player Minimum Effort Game
9
9
10
- >>> filepath = "./tests/gam_files/minimum_effort_game.gam"
11
- >>> nfg = qe_nfg_from_gam_file(filepath)
10
+ >>> import os
11
+ >>> import quantecon.game_theory as gt
12
+ >>> filepath = os.path.dirname(gt.__file__)
13
+ >>> filepath += "/tests/game_files/minimum_effort_game.gam"
14
+ >>> nfg = gt.from_gam(filepath)
12
15
>>> print(nfg)
13
16
3-player NormalFormGame with payoff profile array:
14
17
[[[[ 1., 1., 1.], [ 1., 1., -9.], [ 1., 1., -19.]],
24
27
[[-19., -19., 1.], [ -8., -8., 2.], [ 3., 3., 3.]]]]
25
28
26
29
"""
30
+ import numpy as np
31
+ from .normal_form_game import Player , NormalFormGame
27
32
28
- from .normal_form_game import NormalFormGame
29
- from itertools import product
30
33
34
+ def str2num (s ):
35
+ if '.' in s :
36
+ return float (s )
37
+ return int (s )
31
38
32
- def qe_nfg_from_gam_file (filename : str ) -> NormalFormGame :
39
+
40
+ class GAMReader :
41
+ """
42
+ Reader object that converts a game in GameTracer gam format into
43
+ a NormalFormGame.
44
+
45
+ """
46
+ @classmethod
47
+ def from_file (cls , file_path ):
48
+ """
49
+ Read from a gam format file.
50
+
51
+ Parameters
52
+ ----------
53
+ file_path : str
54
+ Path to gam file.
55
+
56
+ Returns
57
+ -------
58
+ NormalFormGame
59
+
60
+ Examples
61
+ --------
62
+ Save a gam format string in a temporary file:
63
+
64
+ >>> import tempfile
65
+ >>> fname = tempfile.mkstemp()[1]
66
+ >>> with open(fname, mode='w') as f:
67
+ ... f.write(\" \" \" \\
68
+ ... 2
69
+ ... 3 2
70
+ ...
71
+ ... 3 2 0 3 5 6 3 2 3 2 6 1\" \" \" )
72
+
73
+ Read the file:
74
+
75
+ >>> g = GAMReader.from_file(fname)
76
+ >>> print(g)
77
+ 2-player NormalFormGame with payoff profile array:
78
+ [[[3, 3], [3, 2]],
79
+ [[2, 2], [5, 6]],
80
+ [[0, 3], [6, 1]]]
81
+
82
+ """
83
+ with open (file_path , 'r' ) as f :
84
+ string = f .read ()
85
+ return cls ._parse (string )
86
+
87
+ @classmethod
88
+ def from_url (cls , url ):
89
+ """
90
+ Read from a URL.
91
+
92
+ """
93
+ import urllib .request
94
+ with urllib .request .urlopen (url ) as response :
95
+ string = response .read ().decode ()
96
+ return cls ._parse (string )
97
+
98
+ @classmethod
99
+ def from_string (cls , string ):
100
+ """
101
+ Read from a gam format string.
102
+
103
+ Parameters
104
+ ----------
105
+ string : str
106
+ String in gam format.
107
+
108
+ Returns
109
+ -------
110
+ NormalFormGame
111
+
112
+ Examples
113
+ --------
114
+ >>> string = \" \" \" \\
115
+ ... 2
116
+ ... 3 2
117
+ ...
118
+ ... 3 2 0 3 5 6 3 2 3 2 6 1\" \" \"
119
+ >>> g = GAMReader.from_string(string)
120
+ >>> print(g)
121
+ 2-player NormalFormGame with payoff profile array:
122
+ [[[3, 3], [3, 2]],
123
+ [[2, 2], [5, 6]],
124
+ [[0, 3], [6, 1]]]
125
+
126
+ """
127
+ return cls ._parse (string )
128
+
129
+ @staticmethod
130
+ def _parse (string ):
131
+ tokens = string .split ()
132
+
133
+ N = int (tokens .pop (0 ))
134
+ nums_actions = tuple (int (tokens .pop (0 )) for _ in range (N ))
135
+ payoffs = np .array ([str2num (s ) for s in tokens ])
136
+
137
+ na = np .prod (nums_actions )
138
+ payoffs2d = payoffs .reshape (N , na )
139
+ players = [
140
+ Player (
141
+ payoffs2d [i , :].reshape (nums_actions , order = 'F' ).transpose (
142
+ list (range (i , N )) + list (range (i ))
143
+ )
144
+ ) for i in range (N )
145
+ ]
146
+
147
+ return NormalFormGame (players )
148
+
149
+
150
+ class GAMWriter :
151
+ """
152
+ Writer object that converts a NormalFormgame into a game in
153
+ GameTracer gam format.
154
+
155
+ """
156
+ @classmethod
157
+ def to_file (cls , g , file_path ):
158
+ """
159
+ Save the GameTracer gam format string representation of the
160
+ NormalFormGame `g` to a file.
161
+
162
+ Parameters
163
+ ----------
164
+ g : NormalFormGame
165
+
166
+ file_path : str
167
+ Path to the file to write to.
168
+
169
+ """
170
+ with open (file_path , 'w' ) as f :
171
+ f .write (cls ._dump (g ) + '\n ' )
172
+
173
+ @classmethod
174
+ def to_string (cls , g ):
175
+ """
176
+ Return a GameTracer gam format string representing the
177
+ NormalFormGame `g`.
178
+
179
+ Parameters
180
+ ----------
181
+ g : NormalFormGame
182
+
183
+ Returns
184
+ -------
185
+ str
186
+ String representation in gam format.
187
+
188
+ """
189
+ return cls ._dump (g )
190
+
191
+ @staticmethod
192
+ def _dump (g ):
193
+ s = str (g .N ) + '\n '
194
+ s += ' ' .join (map (str , g .nums_actions )) + '\n \n '
195
+
196
+ for i , player in enumerate (g .players ):
197
+ payoffs = np .array2string (
198
+ player .payoff_array .transpose (
199
+ list (range (g .N - i , g .N )) + list (range (g .N - i ))
200
+ ).ravel (order = 'F' ))[1 :- 1 ]
201
+ s += ' ' .join (payoffs .split ()) + ' '
202
+
203
+ return s .rstrip ()
204
+
205
+
206
+ def from_gam (filename : str ) -> NormalFormGame :
33
207
"""
34
208
Makes a QuantEcon Normal Form Game from a gam file.
35
209
@@ -51,32 +225,22 @@ def qe_nfg_from_gam_file(filename: str) -> NormalFormGame:
51
225
http://dags.stanford.edu/Games/gametracer.html
52
226
53
227
"""
54
- with open (filename , 'r' ) as file :
55
- lines = file .readlines ()
56
- combined = [
57
- token
58
- for line in lines
59
- for token in line .split ()
60
- ]
228
+ return GAMReader .from_file (filename )
61
229
62
- i = iter (combined )
63
- players = int (next (i ))
64
- actions = [int (next (i )) for _ in range (players )]
65
230
66
- nfg = NormalFormGame (actions )
231
+ def to_gam (g , file_path = None ):
232
+ """
233
+ Write a NormalFormGame to a file in gam format.
67
234
68
- entries = [
69
- {
70
- tuple (reversed (action_combination )): float (next (i ))
71
- for action_combination in product (
72
- * [range (a ) for a in actions ])
73
- }
74
- for _ in range (players )
75
- ]
235
+ Parameters
236
+ ----------
237
+ g : NormalFormGame
76
238
77
- for action_combination in product (* [range (a ) for a in actions ]):
78
- nfg [action_combination ] = tuple (
79
- entries [p ][action_combination ] for p in range (players )
80
- )
239
+ file_path : str, optional(default=None)
240
+ Path to the file to write to. If None, the result is returned as
241
+ a string.
81
242
82
- return nfg
243
+ """
244
+ if file_path is None :
245
+ return GAMWriter .to_string (g )
246
+ return GAMWriter .to_file (g , file_path )
0 commit comments