xkcd-Name-Dominoes.ipynb

This commit is contained in:
Peter Norvig authored and GitHub committed 2018-03-22 10:59:18 -07:00
1 parent 7db423307e
commit 06e7a9f520
1 file changed
+136 -105
+136 -105
View File
@@ -8,13 +8,12 @@
"# `xkcd` Name Dominoes\n",
"\n",
"The March 21, 2018 `xkcd` comic number 1970 was [Name Dominoes](https://xkcd.com/1970/): domino tiles laid out in a legal array,\n",
"but with each tile having names of famous poeple rather than numbers. In regular [dominoes](https://en.wikipedia.org/wiki/Dominoes), each tile has two halves, and a tile can be legally placed only if one half is adjacent to another tile with the same number/name, and neither half is adjacent to any tile with a different number/name. (The very first tile in a game has no adjacent tiles, so it can be placed anywhere.) `xkcd 1970` makes two exceptions to the rules: (1) some tiles have three names, and matches are allowed against the middle name, and (2) approximate matches are allowed, like \"Amy\" and \"Aimee\".\n",
"but with each tile having names of famous poeple rather than numbers. In regular [dominoes](https://en.wikipedia.org/wiki/Dominoes), each tile has two halves, and a tile can be legally placed only if one half is adjacent to another tile with the same number/name, and neither half is adjacent to any tile with a different number/name. (The very first tile in a game has no adjacent tiles, so it can be placed anywhere.) `xkcd 1970` also allows some tiles to three names, with matches allowed against the middle name; I won't allow that.\n",
"\n",
"I will write a function to lay out dominoes in a random, legal array. I won't implement the two `xkcd` exceptions. I'll start with two key data structures:\n",
"I will write a function to lay out dominoes in a random, legal array. I'll start with two key data structures:\n",
"\n",
"- **`Board(n)`**: Creates an [n × n] 2-dimensional array of locations; each location holds as a value one half of a tile, or it can be `empty`, or it can be a `border`, meaning nothing can be placed there.\n",
"- **`tiles(text)`**: a function to create a list of tiles, each of which is a 2-element list, like `['a', 'b']`, indicating the two halves. The input is a string, consisting of space-separated tiles, where the two halves of the tile are separated by a `|` character.\n",
"\n"
"- **`tiles(text)`**: a function to create a list of tiles, each of which is a 2-element list, like `['a', 'b']`, indicating the two halves. The input is a string, consisting of comma-and/or-space-separated tiles, where the two halves of the tile are separated by a `|` character."
]
},
{
@@ -39,14 +38,7 @@
" \n",
" def get(self, loc): return self[loc[1]][loc[0]]\n",
" \n",
" def put(self, loc, value): self[loc[1]][loc[0]] = value\n",
" \n",
"def tiles(text):\n",
" \"tiles('a|b b|c') => [['a', 'b'], ['b', 'c']\"\n",
" return [tile.split('|') for tile in text.split()]\n",
"\n",
"\n",
"names1 = tiles('Bo|Ja Ja|Ry Ja|Po Ry|Ke Gr|Ke Gr|Ho Bo|Po Ry|Ja')"
" def put(self, loc, value): self[loc[1]][loc[0]] = value"
]
},
{
@@ -80,44 +72,17 @@
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [
{
"data": {
"text/plain": [
"[['Bo', 'Ja'],\n",
" ['Ja', 'Ry'],\n",
" ['Ja', 'Po'],\n",
" ['Ry', 'Ke'],\n",
" ['Gr', 'Ke'],\n",
" ['Gr', 'Ho'],\n",
" ['Bo', 'Po'],\n",
" ['Ry', 'Ja']]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"names1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now I need a strategy to fill the board with tiles, legally. I will randomly place dominoes one at a time, and I will *not* consider removing a tile from the board and backtracking; this is a greedy search. Some more concepts:\n",
"def tiles(text):\n",
" \"tiles('Bo|Ja, Ja|Po') => [['Bo', 'Ja'], ['Ja', 'Po']\"\n",
" return [tile.split('|') for tile in split(text)]\n",
"\n",
"- **`frontier`**: I'll maintain a *frontier*, a set of locations that are adjacent to tiles on the board, and thus are candidates for placing new tiles.\n",
"- **`dominoes(tiles)`**: places a random tile for the first tile, then repeatedly calls `place1` to legally place an additional tile, stopping when either there is no `frontier` left (meaning no place to put a tile) or no `tiles` left to place.\n",
"- **`place1(tiles, board, frontier)`**: find a location in the frontier, such that some tile can legally put one of its halves there, and the other half on an adjacent location; when found, `put` the tile there, and remove it from `tiles`.\n",
"- **`legal(value, loc, board)`**: a value can be placed in a location if the location is empty, and no neighboring location has a different value (but it is ok if a neighbor is empty or is a border).\n",
"- **`neighbors(loc)`**: returns the four neighbors of a location.\n",
"- **`put(board, loc0, loc1, tile, frontier)`**: places a tile on the board, it accomplishes this by making two calls to the board's `put` method, one for each half of the tile. The `put` function also updates the `frontier` to say that the just-covered locations are no longer in the frontier, but the empty neighbors of the tile are.\n",
"- **`shuffle(sequence)`**: used to randomize lists; calls `random.shuffle` and returns the result."
"def split(text): return text.replace(',', ' ').split()\n",
"\n",
"tiles1 = tiles('Bo|Ja, Ja|Po, Ja|Ry, Ry|Ke, Gr|Ke, Gr|Ho')"
]
},
{
@@ -130,14 +95,12 @@
{
"data": {
"text/plain": [
"[['--', '--', '--', '--', '--', '--', '--', '--'],\n",
" ['--', ' ', 'Po', 'Ja', 'Ja', 'Ry', ' ', '--'],\n",
" ['--', ' ', 'Po', ' ', ' ', 'Ry', 'Ke', '--'],\n",
" ['--', ' ', 'Bo', 'Bo', ' ', ' ', 'Ke', '--'],\n",
" ['--', ' ', ' ', 'Ja', ' ', ' ', 'Gr', '--'],\n",
" ['--', ' ', ' ', 'Ja', 'Ry', ' ', 'Gr', '--'],\n",
" ['--', ' ', ' ', ' ', ' ', ' ', 'Ho', '--'],\n",
" ['--', '--', '--', '--', '--', '--', '--', '--']]"
"[['Bo', 'Ja'],\n",
" ['Ja', 'Po'],\n",
" ['Ja', 'Ry'],\n",
" ['Ry', 'Ke'],\n",
" ['Gr', 'Ke'],\n",
" ['Gr', 'Ho']]"
]
},
"execution_count": 4,
@@ -145,10 +108,54 @@
"output_type": "execute_result"
}
],
"source": [
"tiles1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now I need a strategy to fill the board with tiles, legally. I will randomly place dominoes one at a time, and I will *not* consider removing a tile from the board and backtracking; this is a greedy search. Some more concepts:\n",
"\n",
"- **`frontier`**: I'll maintain a *frontier*, a set of locations that are adjacent to tiles on the board, and thus are candidates for placing new tiles.\n",
"- **`dominoes(tiles)`**: places a random tile for the first tile, then repeatedly calls `place1` to legally place an additional tile, stopping when either there is no `frontier` left (meaning no place to put a tile) or no `tiles` left to place.\n",
"- **`place1(tiles, board, frontier)`**: find a location in the frontier, such that some tile can legally put one of its halves there, and the other half on an adjacent location; when found, `put` the tile there, and remove it from `tiles`.\n",
"- **`legal(value, loc, board)`**: a value can be placed if the location is empty, and the value matches all the neighboring location values.\n",
"- **`legal_match(value, nbrval)`**: a value matches if the neighbor is empty or a border, or is equal to the value.\n",
"- **`neighbors(loc)`**: returns the four neighbors of a location.\n",
"- **`put(board, loc0, loc1, tile, frontier)`**: places a tile on the board, it accomplishes this by making two calls to the board's `put` method, one for each half of the tile. The `put` function also updates the `frontier` to say that the just-covered locations are no longer in the frontier, but the empty neighbors of the tile are.\n",
"- **`shuffle(sequence)`**: used to randomize lists; calls `random.shuffle` and returns the result."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[['--', '--', '--', '--', '--', '--', '--'],\n",
" ['--', ' ', ' ', 'Ja', 'Ja', 'Bo', '--'],\n",
" ['--', ' ', 'Ry', 'Ry', ' ', ' ', '--'],\n",
" ['--', ' ', 'Ke', ' ', ' ', ' ', '--'],\n",
" ['--', ' ', 'Ke', 'Gr', ' ', ' ', '--'],\n",
" ['--', ' ', ' ', 'Gr', 'Ho', ' ', '--'],\n",
" ['--', '--', '--', '--', '--', '--', '--']]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import random\n",
"\n",
"def dominoes(tiles, n=6):\n",
"def dominoes(tiles, n=24):\n",
" \"Place as many tiles on board as possible, legally and randomly.\"\n",
" tiles = shuffle(list(tiles))\n",
" board = Board(n)\n",
@@ -175,9 +182,13 @@
"def legal(value, loc, board):\n",
" \"Is it legal to place this value on this location on board?\"\n",
" return (board.get(loc) is empty and\n",
" all(board.get(nbr) in (empty, border, value) \n",
" all(legal_match(value, board.get(nbr)) \n",
" for nbr in neighbors(loc)))\n",
"\n",
"def legal_match(value, nbrval):\n",
" \"Does this value match the neighbor's value?\"\n",
" return nbrval in (empty, border, value)\n",
" \n",
"def neighbors(loc):\n",
" \"Neighbors of this location.\"\n",
" x, y = loc\n",
@@ -197,7 +208,7 @@
" random.shuffle(seq)\n",
" return seq\n",
" \n",
"dominoes(names1)"
"dominoes(tiles1, 5)"
]
},
{
@@ -214,7 +225,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 6,
"metadata": {
"collapsed": true
},
@@ -223,8 +234,8 @@
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"\n",
"def plot_board(board, size=6):\n",
" plt.figure(figsize=(size, size))\n",
"def plot_board(board, figsize=6):\n",
" plt.figure(figsize=(figsize, figsize))\n",
" plt.axis('off') \n",
" plt.axis('equal')\n",
" for (x0, y0, x1, y1) in board.boxes:\n",
@@ -263,16 +274,16 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\nGOEGGEa4AYYRboBhhBtgGOEGGEa4AYYRboBhhBtgGOEGGEa4AYYRboBhhBtgGOEGGEa4AYYRboBh\nhBsOsKo6WlWXqupsVZ2vqq+v3sR6wg0H34XuPtnddyT5YFV9aPUg1hJuGKKqjiR5U5I/VdWXtk/g\nT1XVx1ZvY38dWT0AeFXHqupskn9KcjHJTUmOJ7kzyduS/EdV/bC7L6+byH5y4oaD78qtkvcneTbJ\nrUme7C3PJ/lDkncsXci+cuKGWS4leU+S26uqsnXifmeSPy5dxb4Sbjj4rtwqqSQvJLkvyQ1Jzmfr\nu+bPuU1yuFR3r94AG2M7sOnuk2uXXL9NuIZN5x43wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wA\nwwg3wDDCDTCMcAMMI9wAwwg3wDAe67oBquozSU6v3rFLt2z/+9mlK3bvRJJU1fOrh+zCW5L8evUI\nXp4T92Y4neS21SN26Z+3/wFehRP35rg4+fnJV06ok69hU1x5HjcHlxM3wDDCDTCMcAMMI9wAwwg3\nwDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wA\nwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcPOyqupoVf149Y7dmH4N2/svVdXZqjpfVV9fvYn1hBsO\nvgvdfbK770jywar60OpBrCXcvKqqOl1V57ZPfN+qqlq96VptyDUcSfKmJH+qqi9tX8tTVfWx1dvY\nX8LNTjzW3Se2T3w3Jjm+etB1mHwNx6rqbJJfJPmvJDdla/+dST6a5KtV5c/yIeLNZieOV9VPqupc\nkg8neffqQddh8jVcuVXy/iTPJrk1yZO95fkkf0jyjqUL2VdHVg9ghAeT3NPdz1bVI0nG3WbIZlxD\nklxK8p4kt2/f7nlbkncm+ePSVewr4eaVVJKXkjyc5PGq+uXiPddjE67hyq2SSvJCkvuS3JDkfLa+\na/5cd19eN4/9Vt29egO7tP2HOt19co9f9+4kn+juT+3l677Mf+v5JOnut+/x6+7bNWyK1+rrib3j\nxM1VVdXpJPcn+fTqLddrE64BrsaJewNswgnptTpxc+024etp0/mpEoBhhBtgGOEGGEa4AYYRboBh\nhBtgGOEGGEa4AYYRboBhhBtgGOEGGMZDpjgo3pb8/3MyWOq2JBdXj+DlOXED/+hikjOrR/DynLg5\nKM4lnkgHO+HEDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCM\ncAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcHNVVXW0qi5V1dmq\neqqqPrt6E7DlyOoBHGgXuvtUVb0+yS+q6qHu/svqUXDYOXGzE29O8oYkp6rqa1c+WVWPV9V7182C\nw0m4eSXHqupckt8m+UaSHyT5SFW9sarel+TF7v7N0oVwCAk3r+RCd59IciLJqe6+nOTRJB9P8skk\n3145Dg4r97h5Vd39dFX9rqruzVasv5PkpiRfXjoMDiknbnbqq0ke6O7fJflrkie6+2+LN8Gh5MTN\nVXX3M0lO/d3HP09y1/aHLyV5aMEsIE7cXIOquqGq/j3J77v7V6v3wGHlxM2Obd8auXf1DjjsnLgB\nhhFugGGEG2AY4QYYRrgBhhFugGGEG2AY4QYYRrgBhhFugGGEG2AYzyrZDCeSpKrOLt6xG7clubh6\nBEzgxM1BcTHJmdUjYILq7tUbALgGTtwAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3\nwDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wA\nwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAwwg3wDDCDTCMcAMMI9wAw/wvWmtXa1IdtysA\nAAAASUVORK5CYII=\n",
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAFpCAYAAACMK9MWAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAADMBJREFUeJzt3V+Ipfddx/HP12xSGxJTQjVWqU1VTPEPWQjYNiTdRBYq\nKQjBgrDFSqstiDctIlRRvBAhF5ZQNF40LdYWV2OLNKkYMKHZQGka7cJWS7GlQmqxtVZIqKm5yJ+v\nF3PWbrO7sxszu895vvN6wZCZc7Hz4eyZ9/7mmd0n1d0BYP2+Z+kBAOwNQQcYQtABhhB0gCEEHWAI\nQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEE\nHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0\ngCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtAB\nhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcY\nQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAI\nQQcYQtABhhB0gCEEHWCIA0sPAHihquqdSY4sveMFOtrd77+Qn8AJHVijI0kOLj3iBTiYi/AHkBM6\nsFYnuvuWpUecj6o6djE+jxM6wBCCDjCEoAMMIegAQwg6wBCCDjCEoAMMIegAQwg6wBCCDjCEoAMM\nIegAQwg6wBCCDjCEoAMMIegAQwg6wBCCDjCEoAMMIegAQwg6wBCCDuxbVXVtVT34vMe+vNSeF0vQ\nAYY4sPQAgG1TVdck+VCSy5N8O8mvdPc3Fx11HpzQgf3uhqo6dvJt89hvJ/nL7j6U5K82H289QQf2\nu+PdfcvJt81j1yX59Ob9Tyd5zSLLXiBBBzjdF5PcuHn/xs3HW881dIDT3ZHkz6vq15L8T5K3Lrzn\nvAg6sG9192NJDj/vsR/fvPvGiz7oRXLJBWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAI\nQQcYQtABhljFvVyq6p1Jjiy9Y6hXbP779UVXzHW0u9+/9IjzsbKvs4NJvrH0iG2zlhP6kez8BrL3\nfmzzxt47mPUEMlnX19kVSa5ZesS2WcUJfePEKTefZ49U1RNJ4rnde6f832/WZBVfZydft3y3tZzQ\nATgHQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYQtABhhB0\ngCEEHWAIQQcYQtABhhB0gCEEHWAIQQcYYl8FvaqurarHq+pYVX22qo4svelcNpsfXHrH+VjL1ue9\nDh6tqnctvWmSqrq+qu6vqoer6lNVdXdVXbr0rrNZ297dHFh6wAKOd/fhqvq+JP9UVX/d3c8sPYqL\n7uTr4JIkX6iqu7v720uPWruquirJR5Lc3t3/unnspiSXJHl68/El3f3sciu/Y217z2VfndBP1d3f\nSvIfSd6yOaU9UlW/t/Sus6mqI5sTxCNV9YGqqqU3nc2atia5PMllSQ5X1ftOPlhVD1TVq5abtVpv\nSnLfyTgmSXd/KskPVtU/VtVHkty92LrTrW3vrvZt0Kvqh5Nck+R3k7wxyY1JDlXV9YsOO7v7uvtQ\nd78+yZVJbl560C7WsPWGqno4yVeT3JXkE0luqqqXVNWrkzzT3V9ZdOE6vTI7z2mq6vs3l7U+n+Tl\nSa5N8hvd/fYF9z3f2vbuaj8G/Yaqeig732a9J8k3uvuJ7u4kn0ly3aLrzu7mqvrkJkKvzc4LcVut\nYevx7j6U5FCSw939XJKPJ7k9yduSfHDJcSv21Wx+v7v7m919S5LPJvneJJ/ffGe8Tda2d1f7MejH\nu/vW7v65JB9Nck1VvWxzWeB1Sb647LyzuiPJWzYRejTJNl/GWM3W7v5ckq9V1W3Zifjbk9yW5N5F\nh63X3yX5har60VMeO/mzum28Dr22vbvajz8U/T/d/VxV/VaSv0/yXJL7N1/g26Sy88L6cJIHqupf\nFt6zmzVtPdWdSe7q7jdU1VNJHurup5cetUbd/URVvTXJn1bVS5M8leTfkmzlD5zXtvdcaudKw3ar\nqmNJsvl2aF+pqluT/PKFuo5XVU8kSXe/bA9+rQu69WKoqr9J8p7u/tIe/FrHkvW8bte0dy9ftxfD\nxXpu9/UJfdtt/p78u5O8Y+kt57KmrWey+XvH9yZ5bC9iDksQ9C3W3UeTHF16x/lY09Yz2VxiuW3p\nHfBi7McfigKMJOgAQwg6wBCCDjCEoAMMIegAQwg6wBCCDjCEoAMMsZZ/KXoo+c79EFbgFdm51/oa\n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\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x109e2a6d8>"
"<matplotlib.figure.Figure at 0x111873cf8>"
]
},
"metadata": {},
@@ -280,7 +291,7 @@
}
],
"source": [
"plot_board(dominoes(names1))"
"plot_board(dominoes(tiles1))"
]
},
{
@@ -289,43 +300,10 @@
"source": [
"# Scaling Up Names\n",
"\n",
"Now let's try with more tiles, with names taken (mostly) from `xkcd 1970`.\n",
"Now let's try with more tiles, with names taken (mostly) from `xkcd 1970`, and with approximate matches, like \"Amy\" matching \"Aimee\".\n",
"\n",
"I could modify the program to back up if it got stuck before placing all the tiles, but instead I'll just add a new function, `best_dominoes(tiles, repeat=R)` that calls `dominoes` R times, and returns (and optionally prints) the\n",
"board that has placed the most tiles (which we can measure with `len(board.boxes)`)."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"names2 = tiles(\n",
" 'The|Rock Chris|Rock Rock|Hudson Chris|Isaac Isaac|Newton Olivia|Newton Isaac|Hayes '\n",
" 'Shawn|Hayes Wallace|Shawn Charles|Wallace George|Wallace Charles|Manson Ray|Charles '\n",
" 'Rachel|Ray Ray|Allen Tim|Allen Lily|Allen Tim|Cook James-Earl|Ray James-Earl|Jones '\n",
" 'Man|Ray Bat|Man Super|Man Tim|Howard Ron|Howard Ron|Paul Paul|Allen Rand|Paul Ayn|Rand '\n",
" 'Paul|Ryan Jack|Ryan Debby|Ryan Paul|Simon Carly|Simon John|Kelly Megyn|Kelly John|Henry '\n",
" 'Grace|Kelly Grace|Jones Grace|Hopper Jack|Ma Ma|Bell Yo-Yo|Ma Jack|Ruby Jack|Russell '\n",
" 'Charles|Parker Marilyn|Manson Marilyn|Monroe James|Monroe George|Bush George|Clinton Bill|Clinton '\n",
" 'Jack|White Meg|Ryan Meg|White Barry|White Walter|White Betty|White Betty|Ford Henry|Ford Harrison|Ford '\n",
" 'Tommy|John John|Irving John|Kerry Kerry|Washington Jimmy|John John|Adams Amy|Adams Amy|Man '\n",
" 'Benjamin|Harrison Benjamin|Franklin Aretha|Franklin Franklin|Graham James|Garfield Garfield| '\n",
" 'Kristen|Bell Kristen|Stewart Martha|Stewart Martha|Washington Wsshington|Irving John|Irving '\n",
" 'Jimmy|Buffett Jimmy|Jones Warren|Buffett Elizabeth|Warren '\n",
" 'Kevin|Bacon Kevin|Kline Kevin|Love Kevin|Smith Kevin|Costner Will|Smith Tommy|Lee Robert-E|Lee '\n",
" 'John|Wayne Wayne|Newton Wayne|Knight Wayne|Howard Wayne|Brady Tommy|Brady James|Brady')\n",
"\n",
"def best_dominoes(tiles, n=20, size=16, verbose=True, repeat=300):\n",
" board = max((dominoes(tiles, n) for _ in range(repeat)), \n",
" key=lambda board: len(board.boxes))\n",
" if verbose:\n",
" print(len(board.boxes), '/', len(tiles), 'tiles placed')\n",
" plot_board(board, size)\n",
" return board"
"With many names, the program can get stuck before it has placed many tiles. I could modify the program to *back up* in this case, but it will be easier to just add a new function, `best_dominoes(tiles, repeat=R)` that calls `dominoes` R times, and returns (and optionally displays) the\n",
"board that has placed the most tiles out of those R boards (as measured by `len(board.boxes)`)."
]
},
{
@@ -334,19 +312,72 @@
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"tiles2 = tiles(\"\"\"\n",
" The|Rock, Chris|Rock, Rock|Hudson, Chris|Isaac, Isaac|Newton, Olivia|Newton-John, Isaac|Hayes, \n",
" Sean|Hayes, Wallace|Shawn, Charles|Wallace, George|Wallace, Charles|Manson, Ray|Charles, \n",
" Rachel|Ray, Ray|Allen, Tim|Allen, Lily|Allen, Tim|Cook, James-Earl|Ray, James-Earl|Jones, \n",
" Man|Ray, Ray|Mann, Bat|Man, Super|Man, Tim|Howard, Ron|Howard, Ron|Paul, Paul|Allen, \n",
" Rand|Paul, Ayn|Rand, Paul|Ryan, Jack|Ryan, Debby|Ryan, Paul|Simon, Carly|Simon, John|Kelly, \n",
" Megyn|Kelly, John|Henry, Grace|Kelly, Grace|Jones, Grace|Hopper, Jack|Ma, Ma|Bell, Yo-Yo|Ma, \n",
" Jack|Ruby, Jack|Russell, Marilyn|Manson, Marilyn|Monroe, James|Monroe, George|Bush, \n",
" George|Clinton, Bill|Clinton, Jack|White, Meg|Ryan, Meg|White, Barry|White, Walter|White, \n",
" Betty|White, Charlie|Parker, Tommy|John, John|Kerry, Kerry|Washington, Jimmy|John, \n",
" John|Adams, Amy|Adams, Aimee|Man, Benjamin|Harrison, Benjamin|Franklin, Aretha|Franklin, \n",
" Franklin|Graham, James|Garfield, Garfield|, Kristen|Bell, Kristen|Stewart, Martha|Stewart, \n",
" Martha|Washington, Wsshington|Irving, John|Irving, Jimmy|Buffett, Jimmy|Jones, Warren|Buffett, \n",
" Elizabeth|Warren, Betty|Ford, Henry|Ford, Harrison|Ford, Kevin|Bacon, Kevin|Kline, Kevin|Love, \n",
" Kevin|Smith, Kevin|Costner, Will|Smith, Tommy|Lee, Robert-E|Lee, John|Wayne, Wayne|Newton, \n",
" Wayne|Knight, Wayne|Howard, Wayne|Brady, Tom|Brady, James|Brady, Cam|Newton, Cameron|Diaz,\n",
" James|Cameron, Etta|James, John|Oliver, John|Candy, John|Edward, John|Edwards, John|Lewis,\n",
" Huey|Lewis, Huey|Newton, Huey|Long, Long|John-Silver, Prince|, Prince|Harry, Harry|Potter,\n",
" Harry|Truman, Little|Prince, Stuart|Little, Little|Richard, Rich|Little, Chris|Pratt,\n",
" Chris|Evans, Bob|Evans, Tommy|Lee-Jones, Super|Grover\n",
" \"\"\")\n",
"\n",
"def synonyms(text): return [set(group.split('=')) for group in split(text)]\n",
"\n",
"match_groups = synonyms(\"\"\"\n",
" Charlie=Charles, Newton=Newton-John, Sean=Shawn, James=James-Earl=Jimmy, Man=Mann, Amy=Aimee,\n",
" Jack=John=John-Silver, Tom=Tommy, Will=William, Robert-E=Robert=Bob, Cam=Cameron, Oliver=Olivia,\n",
" Edward=Edwards, Rich=Richard, Lee=Lee-Jones, Jones=Lee-Jones\n",
" \"\"\")\n",
"\n",
"def legal_match(value, nbrval):\n",
" \"Does this value match the neighbor's value?\"\n",
" return (nbrval in (empty, border, value) or \n",
" any(value in group and nbrval in group\n",
" for group in match_groups))\n",
"\n",
"def best_dominoes(tiles, n=24, figsize=16, verbose=True, repeat=300):\n",
" board = max((dominoes(tiles, n) for _ in range(repeat)), \n",
" key=lambda board: len(board.boxes))\n",
" if verbose:\n",
" print(len(board.boxes), '/', len(tiles), 'tiles placed')\n",
" plot_board(board, figsize)\n",
" return board"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"84 / 100 tiles placed\n"
"110 / 126 tiles placed\n"
]
},
{
"data": {
"image/png": "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 truncated
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x11342c940>"
"<matplotlib.figure.Figure at 0x11af01320>"
]
},
"metadata": {},
@@ -354,7 +385,7 @@
}
],
"source": [
"best_dominoes(names2);"
"best_dominoes(tiles2);"
]
}
],