|
4 | 4 | "cell_type": "markdown", |
5 | 5 | "metadata": {}, |
6 | 6 | "source": [ |
7 | | - "# Using Gambit with OpenSpiel\n", |
| 7 | + "# Usinops_one_card_poker ops_one_card_pokerambit with OpenSpiel\n", |
8 | 8 | "\n", |
9 | | - "This tutorial demonstrates the interoperability of the Gambit and OpenSpiel Python packages for game-theoretic analysis.\n", |
| 9 | + "This tutorial demonstrates the interoperability of the ops_one_card_pokerambit and OpenSpiel Python packages for game-theoretic analysis.\n", |
10 | 10 | "\n", |
11 | 11 | "Where Gambit is used to compute exact equilibria for games, OpenSpiel provides a variety of iterative learning algorithms that can be used to approximate strategies. Another key distinction is that the PyGambit API allows the user a simple way to define custom games (see tutorials 1-3). This is also possible in OpenSpiel for normal form games, and you can load `.efg` files created from Gambit for extensive form, however some of the key functionality for iterated learning of strategies is only available for games from the built-in library (see the [OpenSpiel documentation](https://openspiel.readthedocs.io/en/latest/games.html)).\n", |
12 | 12 | "\n", |
|
18 | 18 | "4. Comparing the strategies from OpenSpiel against equilibria strategies computed with Gambit\n", |
19 | 19 | "\n", |
20 | 20 | "Note:\n", |
21 | | - "- The version of OpenSpiel used in this tutorial is `1.6.1`. If you are running this tutorial locally, this will be the version installed via the included `requirements.txt` file.\n", |
| 21 | + "- The version of OpenSpiel used in this tutorial is `1.6.9`. If you are running this tutorial locally, this will be the version installed via the included `requirements.txt` file.\n", |
22 | 22 | "- The OpenSpiel code was adapted from the introductory tutorial for the OpenSpiel API on colab [here](https://colab.research.google.com/github/deepmind/open_spiel/blob/master/open_spiel/colabs/OpenSpielTutorial.ipynb)." |
23 | 23 | ] |
24 | 24 | }, |
|
928 | 928 | "output_type": "stream", |
929 | 929 | "text": [ |
930 | 930 | "\n", |
931 | | - "p0:d1 p1:d0\n", |
932 | | - "Agent 0 chooses p0a0\n", |
| 931 | + "p0:d0 p1:d1\n", |
| 932 | + "Agent 0 chooses p0a2\n", |
933 | 933 | "\n", |
934 | | - "p0:d1 p1:d0 p0:a0\n", |
| 934 | + "p0:d0 p1:d1 p0:a2\n", |
935 | 935 | "Agent 1 chooses p1a2\n", |
936 | 936 | "\n", |
937 | | - "p0:d1 p1:d0 p0:a0 p1:a2\n", |
| 937 | + "p0:d0 p1:d1 p0:a2 p1:a2\n", |
938 | 938 | "Rewards: [10.0, 10.0]\n" |
939 | 939 | ] |
940 | 940 | } |
|
976 | 976 | "source": [ |
977 | 977 | "## Extensive form games created with Gambit\n", |
978 | 978 | "\n", |
979 | | - "It's also possible to create an extensive form game in Gambit and export it to OpenSpiel. Here we demonstrate this with the one-card poker game introduced in tutorial 3." |
| 979 | + "It's also possible to create an extensive form game in Gambit and export it to OpenSpiel. Here we demonstrate this with the one-card poker game introduced in tutorial 3:" |
980 | 980 | ] |
981 | 981 | }, |
982 | 982 | { |
983 | 983 | "cell_type": "code", |
984 | | - "execution_count": 28, |
| 984 | + "execution_count": 47, |
| 985 | + "id": "77dc34c8", |
| 986 | + "metadata": {}, |
| 987 | + "outputs": [], |
| 988 | + "source": [ |
| 989 | + "gbt_one_card_poker = gbt.Game.new_tree(\n", |
| 990 | + " players=[\"Alice\", \"Bob\"],\n", |
| 991 | + " title=\"Stripped-Down Poker: a simple game of one-card poker from Reiley et al (2008).\"\n", |
| 992 | + ")\n", |
| 993 | + "\n", |
| 994 | + "gbt_one_card_poker.append_move(\n", |
| 995 | + " gbt_one_card_poker.root,\n", |
| 996 | + " player=gbt_one_card_poker.players.chance,\n", |
| 997 | + " actions=[\"King\", \"Queen\"] # By default, chance actions have equal probabilities\n", |
| 998 | + ")\n", |
| 999 | + "\n", |
| 1000 | + "for node in gbt_one_card_poker.root.children:\n", |
| 1001 | + " gbt_one_card_poker.append_move(\n", |
| 1002 | + " node,\n", |
| 1003 | + " player=\"Alice\",\n", |
| 1004 | + " actions=[\"Bet\", \"Fold\"]\n", |
| 1005 | + " )\n", |
| 1006 | + "\n", |
| 1007 | + "gbt_one_card_poker.append_move(\n", |
| 1008 | + " [gbt_one_card_poker.root.children[\"King\"].children[\"Bet\"], gbt_one_card_poker.root.children[\"Queen\"].children[\"Bet\"]],\n", |
| 1009 | + " player=\"Bob\",\n", |
| 1010 | + " actions=[\"Call\", \"Fold\"]\n", |
| 1011 | + ")\n", |
| 1012 | + "\n", |
| 1013 | + "win_big = gbt_one_card_poker.add_outcome([2, -2], label=\"Win Big\")\n", |
| 1014 | + "win = gbt_one_card_poker.add_outcome([1, -1], label=\"Win\")\n", |
| 1015 | + "lose_big = gbt_one_card_poker.add_outcome([-2, 2], label=\"Lose Big\")\n", |
| 1016 | + "lose = gbt_one_card_poker.add_outcome([-1, 1], label=\"Lose\")\n", |
| 1017 | + "\n", |
| 1018 | + "# Alice folds, Bob wins small\n", |
| 1019 | + "gbt_one_card_poker.set_outcome(gbt_one_card_poker.root.children[\"King\"].children[\"Fold\"], lose)\n", |
| 1020 | + "gbt_one_card_poker.set_outcome(gbt_one_card_poker.root.children[\"Queen\"].children[\"Fold\"], lose)\n", |
| 1021 | + "# Bob sees Alice Bet and calls, correctly believing she is bluffing, Bob wins big\n", |
| 1022 | + "gbt_one_card_poker.set_outcome(gbt_one_card_poker.root.children[\"Queen\"].children[\"Bet\"].children[\"Call\"], lose_big)\n", |
| 1023 | + "\n", |
| 1024 | + "# Bob sees Alice Bet and calls, incorrectly believing she is bluffing, Alice wins big\n", |
| 1025 | + "gbt_one_card_poker.set_outcome(gbt_one_card_poker.root.children[\"King\"].children[\"Bet\"].children[\"Call\"], win_big)\n", |
| 1026 | + "\n", |
| 1027 | + "# Bob does not call Alice's Bet, Alice wins small\n", |
| 1028 | + "gbt_one_card_poker.set_outcome(gbt_one_card_poker.root.children[\"King\"].children[\"Bet\"].children[\"Fold\"], win)\n", |
| 1029 | + "gbt_one_card_poker.set_outcome(gbt_one_card_poker.root.children[\"Queen\"].children[\"Bet\"].children[\"Fold\"], win)" |
| 1030 | + ] |
| 1031 | + }, |
| 1032 | + { |
| 1033 | + "cell_type": "markdown", |
| 1034 | + "id": "4f296f44", |
| 1035 | + "metadata": {}, |
| 1036 | + "source": [ |
| 1037 | + "Create the game in OpenSpiel:" |
| 1038 | + ] |
| 1039 | + }, |
| 1040 | + { |
| 1041 | + "cell_type": "code", |
| 1042 | + "execution_count": 48, |
985 | 1043 | "id": "07340e32", |
986 | 1044 | "metadata": {}, |
987 | 1045 | "outputs": [ |
988 | 1046 | { |
989 | | - "ename": "FileNotFoundError", |
990 | | - "evalue": "[Errno 2] No such file or directory: '../poker.efg'", |
991 | | - "output_type": "error", |
992 | | - "traceback": [ |
993 | | - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", |
994 | | - "\u001b[31mFileNotFoundError\u001b[39m Traceback (most recent call last)", |
995 | | - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[28]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m../poker.efg\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mr\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[32m 2\u001b[39m poker_efg_string = f.read()\n\u001b[32m 3\u001b[39m ops_one_card_poker = pyspiel.load_efg_game(poker_efg_string)\n", |
996 | | - "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/envs/gambitvenv313/lib/python3.13/site-packages/IPython/core/interactiveshell.py:343\u001b[39m, in \u001b[36m_modified_open\u001b[39m\u001b[34m(file, *args, **kwargs)\u001b[39m\n\u001b[32m 336\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m file \u001b[38;5;129;01min\u001b[39;00m {\u001b[32m0\u001b[39m, \u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m}:\n\u001b[32m 337\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 338\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mIPython won\u001b[39m\u001b[33m'\u001b[39m\u001b[33mt let you open fd=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfile\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m by default \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 339\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 340\u001b[39m \u001b[33m\"\u001b[39m\u001b[33myou can use builtins\u001b[39m\u001b[33m'\u001b[39m\u001b[33m open.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 341\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m343\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio_open\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", |
997 | | - "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: '../poker.efg'" |
998 | | - ] |
| 1047 | + "data": { |
| 1048 | + "text/plain": [ |
| 1049 | + "efg_game()" |
| 1050 | + ] |
| 1051 | + }, |
| 1052 | + "execution_count": 48, |
| 1053 | + "metadata": {}, |
| 1054 | + "output_type": "execute_result" |
999 | 1055 | } |
1000 | 1056 | ], |
1001 | 1057 | "source": [ |
1002 | | - "with open(\"../poker.efg\", \"r\") as f:\n", |
1003 | | - " poker_efg_string = f.read()\n", |
1004 | | - " ops_one_card_poker = pyspiel.load_efg_game(poker_efg_string)\n", |
| 1058 | + "ops_one_card_poker = pyspiel.load_efg_game(gbt_one_card_poker.to_efg())\n", |
1005 | 1059 | "ops_one_card_poker" |
1006 | 1060 | ] |
1007 | 1061 | }, |
|
1017 | 1071 | }, |
1018 | 1072 | { |
1019 | 1073 | "cell_type": "code", |
1020 | | - "execution_count": null, |
| 1074 | + "execution_count": 49, |
1021 | 1075 | "id": "c01c4d6f", |
1022 | 1076 | "metadata": {}, |
1023 | 1077 | "outputs": [ |
1024 | 1078 | { |
1025 | 1079 | "data": { |
1026 | 1080 | "text/plain": [ |
1027 | | - "4" |
| 1081 | + "3" |
1028 | 1082 | ] |
1029 | 1083 | }, |
1030 | | - "execution_count": 29, |
| 1084 | + "execution_count": 49, |
1031 | 1085 | "metadata": {}, |
1032 | 1086 | "output_type": "execute_result" |
1033 | 1087 | } |
|
1041 | 1095 | "id": "9986860c", |
1042 | 1096 | "metadata": {}, |
1043 | 1097 | "source": [ |
1044 | | - "The one-card poker game has 4 distinct actions, 2 are for the first player (Alice in the example game): \"Raise\" and \"Fold\", and 2 for the second player (Bob): \"Meet\" and \"Pass\".\n", |
| 1098 | + "The one-card poker game has 4 distinct actions, 2 are for the first player (Alice in the example game): \"Bet\" and \"Fold\", and 2 for the second player (Bob): \"Call\" and \"Fold\".\n", |
1045 | 1099 | "\n", |
1046 | 1100 | "Initialising the game state, we can see the current player at the start is the chance player, who deals the cards:" |
1047 | 1101 | ] |
1048 | 1102 | }, |
1049 | 1103 | { |
1050 | 1104 | "cell_type": "code", |
1051 | | - "execution_count": null, |
| 1105 | + "execution_count": 50, |
1052 | 1106 | "id": "3b9cc43b", |
1053 | 1107 | "metadata": {}, |
1054 | 1108 | "outputs": [ |
|
1058 | 1112 | "0: Chance: 1 King 0.5 Queen 0.5" |
1059 | 1113 | ] |
1060 | 1114 | }, |
1061 | | - "execution_count": 30, |
| 1115 | + "execution_count": 50, |
1062 | 1116 | "metadata": {}, |
1063 | 1117 | "output_type": "execute_result" |
1064 | 1118 | } |
|
1068 | 1122 | "state" |
1069 | 1123 | ] |
1070 | 1124 | }, |
| 1125 | + { |
| 1126 | + "cell_type": "code", |
| 1127 | + "execution_count": 51, |
| 1128 | + "id": "e23df723", |
| 1129 | + "metadata": {}, |
| 1130 | + "outputs": [ |
| 1131 | + { |
| 1132 | + "data": { |
| 1133 | + "text/plain": [ |
| 1134 | + "[0, 1]" |
| 1135 | + ] |
| 1136 | + }, |
| 1137 | + "execution_count": 51, |
| 1138 | + "metadata": {}, |
| 1139 | + "output_type": "execute_result" |
| 1140 | + } |
| 1141 | + ], |
| 1142 | + "source": [ |
| 1143 | + "state.legal_actions()" |
| 1144 | + ] |
| 1145 | + }, |
1071 | 1146 | { |
1072 | 1147 | "cell_type": "markdown", |
1073 | 1148 | "id": "7b0959f9", |
|
1078 | 1153 | }, |
1079 | 1154 | { |
1080 | 1155 | "cell_type": "code", |
1081 | | - "execution_count": null, |
| 1156 | + "execution_count": 52, |
1082 | 1157 | "id": "4dd5d504", |
1083 | 1158 | "metadata": {}, |
1084 | 1159 | "outputs": [ |
1085 | 1160 | { |
1086 | 1161 | "data": { |
1087 | 1162 | "text/plain": [ |
1088 | | - "1: Player: 1 1 Raise Fold" |
| 1163 | + "1: Player: 1 1 Bet Fold" |
1089 | 1164 | ] |
1090 | 1165 | }, |
1091 | | - "execution_count": 31, |
| 1166 | + "execution_count": 52, |
1092 | 1167 | "metadata": {}, |
1093 | 1168 | "output_type": "execute_result" |
1094 | 1169 | } |
|
1098 | 1173 | "state" |
1099 | 1174 | ] |
1100 | 1175 | }, |
| 1176 | + { |
| 1177 | + "cell_type": "code", |
| 1178 | + "execution_count": 53, |
| 1179 | + "id": "be557706", |
| 1180 | + "metadata": {}, |
| 1181 | + "outputs": [ |
| 1182 | + { |
| 1183 | + "data": { |
| 1184 | + "text/plain": [ |
| 1185 | + "[0, 1]" |
| 1186 | + ] |
| 1187 | + }, |
| 1188 | + "execution_count": 53, |
| 1189 | + "metadata": {}, |
| 1190 | + "output_type": "execute_result" |
| 1191 | + } |
| 1192 | + ], |
| 1193 | + "source": [ |
| 1194 | + "state.legal_actions()" |
| 1195 | + ] |
| 1196 | + }, |
1101 | 1197 | { |
1102 | 1198 | "cell_type": "markdown", |
1103 | 1199 | "id": "b4291f07", |
1104 | 1200 | "metadata": {}, |
1105 | 1201 | "source": [ |
1106 | 1202 | "As expected, it's now the first player's (Alice's) turn.\n", |
1107 | | - "Let's have Alice choose to \"Raise\" (action 0):" |
| 1203 | + "Let's have Alice choose to \"Bet\" (action 0):" |
1108 | 1204 | ] |
1109 | 1205 | }, |
1110 | 1206 | { |
1111 | 1207 | "cell_type": "code", |
1112 | | - "execution_count": null, |
| 1208 | + "execution_count": 54, |
1113 | 1209 | "id": "bd15369f", |
1114 | 1210 | "metadata": {}, |
1115 | 1211 | "outputs": [ |
1116 | 1212 | { |
1117 | 1213 | "data": { |
1118 | 1214 | "text/plain": [ |
1119 | | - "3: Player: 2 1 Meet Pass" |
| 1215 | + "3: Player: 2 1 Call Fold" |
1120 | 1216 | ] |
1121 | 1217 | }, |
1122 | | - "execution_count": 32, |
| 1218 | + "execution_count": 54, |
1123 | 1219 | "metadata": {}, |
1124 | 1220 | "output_type": "execute_result" |
1125 | 1221 | } |
|
1139 | 1235 | }, |
1140 | 1236 | { |
1141 | 1237 | "cell_type": "code", |
1142 | | - "execution_count": null, |
| 1238 | + "execution_count": 55, |
1143 | 1239 | "id": "8d81ff6b", |
1144 | 1240 | "metadata": {}, |
1145 | 1241 | "outputs": [ |
1146 | 1242 | { |
1147 | 1243 | "data": { |
1148 | 1244 | "text/plain": [ |
1149 | | - "[2, 3]" |
| 1245 | + "[1, 2]" |
1150 | 1246 | ] |
1151 | 1247 | }, |
1152 | | - "execution_count": 33, |
| 1248 | + "execution_count": 55, |
1153 | 1249 | "metadata": {}, |
1154 | 1250 | "output_type": "execute_result" |
1155 | 1251 | } |
|
1163 | 1259 | "id": "fdb5194f", |
1164 | 1260 | "metadata": {}, |
1165 | 1261 | "source": [ |
1166 | | - "Whereas player 1 (Alice) had the option to \"Raise\" (action 0) and \"Fold\" (action 1), player 2 (Bob) now has the option to \"Meet\" (action 2) or \"Pass\" (action 3).\n", |
1167 | | - "Let's have Bob choose to \"Pass\":" |
| 1262 | + "Player 2 (Bob) now has the option to \"Call\" (action 1) or \"Fold\" (action 2).\n", |
| 1263 | + "Let's have Bob choose to \"Fold\":" |
1168 | 1264 | ] |
1169 | 1265 | }, |
1170 | 1266 | { |
1171 | 1267 | "cell_type": "code", |
1172 | | - "execution_count": null, |
| 1268 | + "execution_count": 56, |
1173 | 1269 | "id": "97913fe5", |
1174 | 1270 | "metadata": {}, |
1175 | 1271 | "outputs": [ |
1176 | 1272 | { |
1177 | 1273 | "data": { |
1178 | 1274 | "text/plain": [ |
1179 | | - "6: Terminal: Alice wins 1 -1" |
| 1275 | + "6: Terminal: Win 1 -1" |
1180 | 1276 | ] |
1181 | 1277 | }, |
1182 | | - "execution_count": 34, |
| 1278 | + "execution_count": 56, |
1183 | 1279 | "metadata": {}, |
1184 | 1280 | "output_type": "execute_result" |
1185 | 1281 | } |
1186 | 1282 | ], |
1187 | 1283 | "source": [ |
1188 | | - "state.apply_action(3)\n", |
| 1284 | + "state.apply_action(2)\n", |
1189 | 1285 | "state" |
1190 | 1286 | ] |
1191 | 1287 | }, |
|
1194 | 1290 | "id": "1bf09576", |
1195 | 1291 | "metadata": {}, |
1196 | 1292 | "source": [ |
1197 | | - "Since Bob passed, Alice takes the small win and we reach a terminal state." |
| 1293 | + "Since Bob Folded, Alice takes the small win and we reach a terminal state." |
1198 | 1294 | ] |
1199 | 1295 | } |
1200 | 1296 | ], |
|
0 commit comments