Skip to content

Commit eedf6ae

Browse files
committed
Add Minesweeper puzzles (51 - 55)
1 parent a8f5b01 commit eedf6ae

File tree

2 files changed

+239
-8
lines changed

2 files changed

+239
-8
lines changed

100-pandas-puzzles-with-solutions.ipynb

Lines changed: 143 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,7 @@
10701070
},
10711071
{
10721072
"cell_type": "code",
1073-
"execution_count": 3,
1073+
"execution_count": null,
10741074
"metadata": {
10751075
"collapsed": false
10761076
},
@@ -1191,7 +1191,7 @@
11911191
},
11921192
{
11931193
"cell_type": "code",
1194-
"execution_count": 37,
1194+
"execution_count": null,
11951195
"metadata": {
11961196
"collapsed": false
11971197
},
@@ -1206,11 +1206,122 @@
12061206
"new_s = new_s.sort_index()"
12071207
]
12081208
},
1209+
{
1210+
"cell_type": "markdown",
1211+
"metadata": {
1212+
"collapsed": true
1213+
},
1214+
"source": [
1215+
"## Minesweeper\n",
1216+
"\n",
1217+
"### Generate the numbers for safe squares in a Minesweeper grid\n",
1218+
"\n",
1219+
"Difficulty: *medium* to *hard*\n",
1220+
"\n",
1221+
"If you've ever used an older version of Windows, there's a good chance you've played with [Minesweeper](https://en.wikipedia.org/wiki/Minesweeper_(video_game). If you're not familiar with the game, imagine a grid of squares: some of these squares conceal a mine. If you click on a mine, you lose instantly. If you click on a safe square, you reveal a number telling you how many mines are found in the squares that are immediately adjacent. The aim of the game is to uncover all squares in the grid that do not contain a mine.\n",
1222+
"\n",
1223+
"In this section, we'll make a DataFrame that contains the necessary data for a game of Minesweeper: coordinates of the squares, whether the square contains a mine and the number of mines found on adjacent squares."
1224+
]
1225+
},
1226+
{
1227+
"cell_type": "markdown",
1228+
"metadata": {},
1229+
"source": [
1230+
"**51**. Let's suppose we're playing Minesweeper on a 5 by 4 grid, i.e.\n",
1231+
"```\n",
1232+
"X = 5\n",
1233+
"Y = 4\n",
1234+
"```\n",
1235+
"To begin, generate a DataFrame `df` with two columns, `'x'` and `'y'` containing every coordinate for this grid. That is, the DataFrame should start:\n",
1236+
"```\n",
1237+
" x y\n",
1238+
"0 0 0\n",
1239+
"1 0 1\n",
1240+
"2 0 2\n",
1241+
"```"
1242+
]
1243+
},
1244+
{
1245+
"cell_type": "code",
1246+
"execution_count": null,
1247+
"metadata": {
1248+
"collapsed": true
1249+
},
1250+
"outputs": [],
1251+
"source": [
1252+
"p = pd.tools.util.cartesian_product([np.arange(X), np.arange(Y)])\n",
1253+
"df = pd.DataFrame(np.asarray(p).T, columns=['x', 'y'])"
1254+
]
1255+
},
1256+
{
1257+
"cell_type": "markdown",
1258+
"metadata": {},
1259+
"source": [
1260+
"**52**. For this DataFrame `df`, create a new column of zeros (safe) and ones (mine). The probability of a mine occuring at each location should be 0.4."
1261+
]
1262+
},
1263+
{
1264+
"cell_type": "code",
1265+
"execution_count": null,
1266+
"metadata": {
1267+
"collapsed": true
1268+
},
1269+
"outputs": [],
1270+
"source": [
1271+
"# One way is to draw samples from a binomial distribution.\n",
1272+
"\n",
1273+
"df['mine'] = np.random.binomial(1, 0.4, X*Y)"
1274+
]
1275+
},
1276+
{
1277+
"cell_type": "markdown",
1278+
"metadata": {},
1279+
"source": [
1280+
"**53**. Now create a new column for this DataFrame called `'adjacent'`. This column should contain the number of mines found on adjacent squares in the grid. \n",
1281+
"\n",
1282+
"(E.g. for the first row, which is the entry for the coordinate `(0, 0)`, count how many mines are found on the coordinates `(0, 1)`, `(1, 0)` and `(1, 1)`.)"
1283+
]
1284+
},
1285+
{
1286+
"cell_type": "code",
1287+
"execution_count": null,
1288+
"metadata": {
1289+
"collapsed": true
1290+
},
1291+
"outputs": [],
1292+
"source": [
1293+
"# Here is one way to solve using merges.\n",
1294+
"# It's not necessary the optimal way, just \n",
1295+
"# the solution I thought of first...\n",
1296+
"\n",
1297+
"df['adjacent'] = \\\n",
1298+
" df.merge(df + [ 1, 1, 0], on=['x', 'y'], how='left')\\\n",
1299+
" .merge(df + [ 1, -1, 0], on=['x', 'y'], how='left')\\\n",
1300+
" .merge(df + [-1, 1, 0], on=['x', 'y'], how='left')\\\n",
1301+
" .merge(df + [-1, -1, 0], on=['x', 'y'], how='left')\\\n",
1302+
" .merge(df + [ 1, 0, 0], on=['x', 'y'], how='left')\\\n",
1303+
" .merge(df + [-1, 0, 0], on=['x', 'y'], how='left')\\\n",
1304+
" .merge(df + [ 0, 1, 0], on=['x', 'y'], how='left')\\\n",
1305+
" .merge(df + [ 0, -1, 0], on=['x', 'y'], how='left')\\\n",
1306+
" .iloc[:, 3:]\\\n",
1307+
" .sum(axis=1)\n",
1308+
" \n",
1309+
"# An alternative solution is to pivot the DataFrame \n",
1310+
"# to form the \"actual\" grid of mines and use convolution.\n",
1311+
"# See https://github.com/jakevdp/matplotlib_pydata2013/blob/master/examples/minesweeper.py\n",
1312+
"\n",
1313+
"from scipy.signal import convolve2d\n",
1314+
"\n",
1315+
"mine_grid = df.pivot_table(columns='x', index='y', values='mine')\n",
1316+
"counts = convolve2d(mine_grid.astype(complex), np.ones((3, 3)), mode='same').real.astype(int)\n",
1317+
"df['adjacent'] = (counts - mine_grid).ravel('F')"
1318+
]
1319+
},
12091320
{
12101321
"cell_type": "markdown",
12111322
"metadata": {},
12121323
"source": [
1213-
"(More exercies to follow...)"
1324+
"**54**. For rows of the DataFrame that contain a mine, set the value in the `'adjacent'` column to NaN."
12141325
]
12151326
},
12161327
{
@@ -1220,7 +1331,35 @@
12201331
"collapsed": true
12211332
},
12221333
"outputs": [],
1223-
"source": []
1334+
"source": [
1335+
"df.loc[df['mine'] == 1, 'adjacent'] = np.nan"
1336+
]
1337+
},
1338+
{
1339+
"cell_type": "markdown",
1340+
"metadata": {},
1341+
"source": [
1342+
"**55**. Finally, convert the DataFrame to grid of the adjacent mine counts: columns are the `x` coordinate, rows are the `y` coordinate."
1343+
]
1344+
},
1345+
{
1346+
"cell_type": "code",
1347+
"execution_count": null,
1348+
"metadata": {
1349+
"collapsed": true
1350+
},
1351+
"outputs": [],
1352+
"source": [
1353+
"df.drop('mine', axis=1)\\\n",
1354+
" .set_index(['y', 'x']).unstack()"
1355+
]
1356+
},
1357+
{
1358+
"cell_type": "markdown",
1359+
"metadata": {},
1360+
"source": [
1361+
"*More exercises to follow soon...*"
1362+
]
12241363
}
12251364
],
12261365
"metadata": {

100-pandas-puzzles.ipynb

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,19 +1021,104 @@
10211021
]
10221022
},
10231023
{
1024-
"cell_type": "code",
1025-
"execution_count": 37,
1024+
"cell_type": "markdown",
10261025
"metadata": {
10271026
"collapsed": false
10281027
},
1028+
"source": [
1029+
"## Minesweeper\n",
1030+
"\n",
1031+
"### Generate the numbers for safe squares in a Minesweeper grid\n",
1032+
"\n",
1033+
"Difficulty: *medium* to *hard*\n",
1034+
"\n",
1035+
"If you've ever used an older version of Windows, there's a good chance you've played with [Minesweeper](https://en.wikipedia.org/wiki/Minesweeper_(video_game). If you're not familiar with the game, imagine a grid of squares: some of these squares conceal a mine. If you click on a mine, you lose instantly. If you click on a safe square, you reveal a number telling you how many mines are found in the squares that are immediately adjacent. The aim of the game is to uncover all squares in the grid that do not contain a mine.\n",
1036+
"\n",
1037+
"In this section, we'll make a DataFrame that contains the necessary data for a game of Minesweeper: coordinates of the squares, whether the square contains a mine and the number of mines found on adjacent squares."
1038+
]
1039+
},
1040+
{
1041+
"cell_type": "markdown",
1042+
"metadata": {},
1043+
"source": [
1044+
"**51**. Let's suppose we're playing Minesweeper on a 5 by 4 grid, i.e.\n",
1045+
"```\n",
1046+
"X = 5\n",
1047+
"Y = 4\n",
1048+
"```\n",
1049+
"To begin, generate a DataFrame `df` with two columns, `'x'` and `'y'` containing every coordinate for this grid. That is, the DataFrame should start:\n",
1050+
"```\n",
1051+
" x y\n",
1052+
"0 0 0\n",
1053+
"1 0 1\n",
1054+
"2 0 2\n",
1055+
"```"
1056+
]
1057+
},
1058+
{
1059+
"cell_type": "code",
1060+
"execution_count": null,
1061+
"metadata": {
1062+
"collapsed": true
1063+
},
1064+
"outputs": [],
1065+
"source": []
1066+
},
1067+
{
1068+
"cell_type": "markdown",
1069+
"metadata": {},
1070+
"source": [
1071+
"**52**. For this DataFrame `df`, create a new column of zeros (safe) and ones (mine). The probability of a mine occuring at each location should be 0.4."
1072+
]
1073+
},
1074+
{
1075+
"cell_type": "code",
1076+
"execution_count": null,
1077+
"metadata": {
1078+
"collapsed": true
1079+
},
1080+
"outputs": [],
1081+
"source": []
1082+
},
1083+
{
1084+
"cell_type": "markdown",
1085+
"metadata": {},
1086+
"source": [
1087+
"**53**. Now create a new column for this DataFrame called `'adjacent'`. This column should contain the number of mines found on adjacent squares in the grid. \n",
1088+
"\n",
1089+
"(E.g. for the first row, which is the entry for the coordinate `(0, 0)`, count how many mines are found on the coordinates `(0, 1)`, `(1, 0)` and `(1, 1)`.)"
1090+
]
1091+
},
1092+
{
1093+
"cell_type": "code",
1094+
"execution_count": null,
1095+
"metadata": {
1096+
"collapsed": true
1097+
},
1098+
"outputs": [],
1099+
"source": []
1100+
},
1101+
{
1102+
"cell_type": "markdown",
1103+
"metadata": {},
1104+
"source": [
1105+
"**54**. For rows of the DataFrame that contain a mine, set the value in the `'adjacent'` column to NaN."
1106+
]
1107+
},
1108+
{
1109+
"cell_type": "code",
1110+
"execution_count": null,
1111+
"metadata": {
1112+
"collapsed": true
1113+
},
10291114
"outputs": [],
10301115
"source": []
10311116
},
10321117
{
10331118
"cell_type": "markdown",
10341119
"metadata": {},
10351120
"source": [
1036-
"(More exercies to follow...)"
1121+
"**55**. Finally, convert the DataFrame to grid of the adjacent mine counts: columns are the `x` coordinate, rows are the `y` coordinate."
10371122
]
10381123
},
10391124
{
@@ -1044,6 +1129,13 @@
10441129
},
10451130
"outputs": [],
10461131
"source": []
1132+
},
1133+
{
1134+
"cell_type": "markdown",
1135+
"metadata": {},
1136+
"source": [
1137+
"*More exercises to follow soon...*"
1138+
]
10471139
}
10481140
],
10491141
"metadata": {
@@ -1062,7 +1154,7 @@
10621154
"name": "python",
10631155
"nbconvert_exporter": "python",
10641156
"pygments_lexer": "ipython3",
1065-
"version": "3.6.0"
1157+
"version": "3.4.5"
10661158
}
10671159
},
10681160
"nbformat": 4,

0 commit comments

Comments
 (0)