|
1070 | 1070 | },
|
1071 | 1071 | {
|
1072 | 1072 | "cell_type": "code",
|
1073 |
| - "execution_count": 3, |
| 1073 | + "execution_count": null, |
1074 | 1074 | "metadata": {
|
1075 | 1075 | "collapsed": false
|
1076 | 1076 | },
|
|
1191 | 1191 | },
|
1192 | 1192 | {
|
1193 | 1193 | "cell_type": "code",
|
1194 |
| - "execution_count": 37, |
| 1194 | + "execution_count": null, |
1195 | 1195 | "metadata": {
|
1196 | 1196 | "collapsed": false
|
1197 | 1197 | },
|
|
1206 | 1206 | "new_s = new_s.sort_index()"
|
1207 | 1207 | ]
|
1208 | 1208 | },
|
| 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 | + }, |
1209 | 1320 | {
|
1210 | 1321 | "cell_type": "markdown",
|
1211 | 1322 | "metadata": {},
|
1212 | 1323 | "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." |
1214 | 1325 | ]
|
1215 | 1326 | },
|
1216 | 1327 | {
|
|
1220 | 1331 | "collapsed": true
|
1221 | 1332 | },
|
1222 | 1333 | "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 | + ] |
1224 | 1363 | }
|
1225 | 1364 | ],
|
1226 | 1365 | "metadata": {
|
|
0 commit comments