xkcd-Name-Dominoes.ipynb

This commit is contained in:
Peter Norvig authored and GitHub committed 2018-03-22 15:31:05 -07:00
1 parent 06e7a9f520
commit d5b3f6128e
1 file changed
+407 -124
+407 -124
View File
@@ -5,15 +5,14 @@
"metadata": {},
"source": [
"<div style=\"text-align:right\">Peter Norvig<br>21 March 2018</div>\n",
"\n",
"# `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` also allows some tiles to three names, with matches allowed against the middle name; I won't allow that.\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.) I will write a function to lay out dominoes in a random, legal array. I'll start with two key data structures:\n",
"\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 comma-and/or-space-separated tiles, where the two halves of the tile are separated by a `|` character."
"- **`Tile`**: a tile is a 2-element tuple, like `('Tim', 'Cook')`, indicating the two halves.\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"
]
},
{
@@ -27,6 +26,8 @@
"empty = ' '\n",
"border = '--'\n",
"\n",
"Tile = tuple\n",
"\n",
"class Board(list):\n",
" \"A board is a 2d array of values.\"\n",
" \n",
@@ -38,7 +39,9 @@
" \n",
" def get(self, loc): return self[loc[1]][loc[0]]\n",
" \n",
" def put(self, loc, value): self[loc[1]][loc[0]] = value"
" def put(self, loc, value): self[loc[1]][loc[0]] = value\n",
" \n",
"tiles1 = [('Bo', 'Ja'), ('Ja', 'Po'), ('Ja', 'Ry'), ('Ry', 'Ke'), ('Gr', 'Ke'), ('Gr', 'Ho')]"
]
},
{
@@ -68,50 +71,6 @@
"Board(4)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def tiles(text):\n",
" \"tiles('Bo|Ja, Ja|Po') => [['Bo', 'Ja'], ['Ja', 'Po']\"\n",
" return [tile.split('|') for tile in split(text)]\n",
"\n",
"def split(text): return text.replace(',', ' ').split()\n",
"\n",
"tiles1 = tiles('Bo|Ja, Ja|Po, Ja|Ry, Ry|Ke, Gr|Ke, Gr|Ho')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[['Bo', 'Ja'],\n",
" ['Ja', 'Po'],\n",
" ['Ja', 'Ry'],\n",
" ['Ry', 'Ke'],\n",
" ['Gr', 'Ke'],\n",
" ['Gr', 'Ho']]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tiles1"
]
},
{
"cell_type": "markdown",
"metadata": {},
@@ -130,7 +89,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 3,
"metadata": {
"collapsed": false
},
@@ -139,15 +98,15 @@
"data": {
"text/plain": [
"[['--', '--', '--', '--', '--', '--', '--'],\n",
" ['--', ' ', ' ', 'Ja', 'Ja', 'Bo', '--'],\n",
" ['--', ' ', 'Ry', 'Ry', ' ', ' ', '--'],\n",
" ['--', ' ', 'Ke', ' ', ' ', ' ', '--'],\n",
" ['--', ' ', 'Ke', 'Gr', ' ', ' ', '--'],\n",
" ['--', ' ', ' ', 'Gr', 'Ho', ' ', '--'],\n",
" ['--', 'Ho', ' ', 'Po', 'Ja', ' ', '--'],\n",
" ['--', 'Gr', 'Gr', ' ', 'Ja', 'Bo', '--'],\n",
" ['--', ' ', 'Ke', ' ', 'Ja', ' ', '--'],\n",
" ['--', ' ', 'Ke', 'Ry', 'Ry', ' ', '--'],\n",
" ['--', ' ', ' ', ' ', ' ', ' ', '--'],\n",
" ['--', '--', '--', '--', '--', '--', '--']]"
]
},
"execution_count": 5,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@@ -225,9 +184,9 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 4,
"metadata": {
"collapsed": true
"collapsed": false
},
"outputs": [],
"source": [
@@ -244,7 +203,8 @@
" for (y, row) in enumerate(board):\n",
" for (x, val) in enumerate(row):\n",
" if val is not border:\n",
" plt.text(x + 0.5, y + 0.3, val, ha='center', fontsize=9)\n",
" plt.text(x + 0.5, y + 0.3, val.replace('-', ' '),\n",
" ha='center', fontsize=8)\n",
" \n",
"class Board(list):\n",
" \"A board is a 2d array of values.\"\n",
@@ -274,16 +234,16 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"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",
"image/png": "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\nRU5ErkJggg==\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x111873cf8>"
"<matplotlib.figure.Figure at 0x10eb19b70>"
]
},
"metadata": {},
@@ -298,12 +258,379 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Scaling Up Names\n",
"# All the Names\n",
"\n",
"Now let's try with more tiles, with names taken (mostly) from `xkcd 1970`, and with approximate matches, like \"Amy\" matching \"Aimee\".\n",
"Now let's try **all** the names from `xkcd 1970`, as reported \n",
"by [explainxkcd](http://www.explainxkcd.com/wiki/index.php/1970:_Name_Dominoes), with a few typo corrections:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"270"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def split_names(text):\n",
" \"For each line of text, create a Tile of ('First Name(s)', 'Lastname').\"\n",
" return [name.rpartition(' ')[0::2]\n",
" for name in text.strip().splitlines()]\n",
" \n",
"tiles1970 = split_names(\"\"\"\n",
"Christian Campbell\n",
"Neve Campbell\n",
"Joe McCarthy\n",
"Eugene McCarthy\n",
"Gene Vincent\n",
"Gene Kelly\n",
"Kate Hudson\n",
"Rock Hudson\n",
"Gordon Brown\n",
"James Brown\n",
"Jon Brown\n",
"John Howard\n",
"Columbo\n",
"Chris Columbus\n",
"Christopher Columbus\n",
"Naomi Campbell\n",
"Joseph Campbell\n",
"Joseph Smith\n",
"Frank Vincent\n",
"John Kelly\n",
"Katherine Johnson\n",
"The Rock\n",
"Chris Rock\n",
"Chris Isaac\n",
"James Newton Howard\n",
"John Wayne\n",
"Howard Stern\n",
"Howard Hunt\n",
"Chris Hughes\n",
"Naomi Watts\n",
"Naomi Klein\n",
"Kevin Kline\n",
"Francis Bacon\n",
"Francis Drake\n",
"Lyndon Johnson\n",
"Oscar the Grouch\n",
"Oscar Isaac\n",
"Isaac Hayes\n",
"Isaac Newton\n",
"Wayne Newton\n",
"Wayne Knight\n",
"Helen Hunt\n",
"Helen Hughes\n",
"James Watt\n",
"James Watt\n",
"Kevin Costner\n",
"Kevin Bacon\n",
"Kevin Love\n",
"Lisa Frank\n",
"Frank Drake\n",
"Drake\n",
"Oscar de la Renta\n",
"Oscar de la Hoya\n",
"Sean Hayes\n",
"Wallace Shawn\n",
"Wayne Howard\n",
"Wayne Brady\n",
"James Brady\n",
"Tom Brady\n",
"Helen Thomas\n",
"Tom Hanks\n",
"Hank Aaron\n",
"Aaron Carter\n",
"Stephen James\n",
"Will Smith\n",
"Kevin Smith\n",
"Kein James\n",
"Garfield\n",
"James Garfield\n",
"Warren Buffett\n",
"Jimmy Buffett\n",
"Warren Beatty\n",
"Elizabeth Warren\n",
"Earl Warren\n",
"Eliabeth Kolbert\n",
"Stephen Colbert\n",
"George Wallace\n",
"Charles Wallace\n",
"James Monroe\n",
"Marilyn Monroe\n",
"Hank Williams\n",
"William C. Williams\n",
"Steve Harvey\n",
"Domino Harvey\n",
"Harvey Milk\n",
"James Saint James\n",
"Etta James\n",
"Jim Jones\n",
"James Earl Jones\n",
"Charlie Parker\n",
"Ray Parker\n",
"Ray Charles\n",
"Charles Manson\n",
"Marilyn Manson\n",
"Robin Williams\n",
"Billy D. Williams\n",
"Will Wright\n",
"Fats Domino\n",
"Bill Clinton\n",
"Jimmy John\n",
"Tom Jones\n",
"Tommy John\n",
"Quincy Jones\n",
"James Earl Ray\n",
"Man Ray\n",
"Rachel Ray\n",
"Ray Allen\n",
"Tim Allen\n",
"Tim Cook\n",
"Tim Howard\n",
"Robin Wright\n",
"Wilbur Wright\n",
"Fatty Arbuckle\n",
"Fat Joe\n",
"George Clinton\n",
"John Kerry\n",
"Kerry Washington\n",
"John Irving\n",
"John Quincy Adams\n",
"John Adams\n",
"Amy Adams\n",
"Aimee Mann\n",
"Super Man\n",
"Bat Man\n",
"Ayn Rand\n",
"Lily Allen\n",
"Paul Allen\n",
"Ron Howard\n",
"Howard Hughes\n",
"Joe Kennedy\n",
"George Bush\n",
"George Wasington\n",
"Wasington Irving\n",
"Martha Wasington\n",
"Ma Rainey\n",
"Jack Ma\n",
"Super Grover\n",
"Jack Black\n",
"Rand Paul\n",
"Paul Ryan\n",
"Paul Simon\n",
"Ron Paul\n",
"John Hughes\n",
"Langston Hughes\n",
"John F. Kennedy\n",
"Little Richard\n",
"Rich Little\n",
"Martha Stewart\n",
"Yo Yo Ma\n",
"Ma Bell\n",
"Grover Cleveland Alexander\n",
"Grover Cleveland\n",
"Jack White\n",
"Jack Ryan\n",
"Debby Ryan\n",
"Carly Simon\n",
"Carly Hughes\n",
"Charles Evans Hughes\n",
"John Williams\n",
"Little John\n",
"Stuart Little\n",
"Potter Stewart\n",
"Kristen Stewart\n",
"Kristen Bell\n",
"Kristen Hooks\n",
"Alexander Graham Bell\n",
"Franklin Graham\n",
"Lloyd Alexander\n",
"Meg White\n",
"Meg Ryan\n",
"Debbie Reynolds\n",
"John Reynolds\n",
"Carly Fiorina\n",
"Grace Lee Boggs\n",
"Wade Boggs\n",
"William Safire\n",
"Prince William\n",
"Little Prince\n",
"Harry Potter\n",
"James Potter\n",
"James Hook\n",
"James Dean\n",
"Aretha Franklin\n",
"Frank Lloyd Wright\n",
"Barry White\n",
"Walter White\n",
"Walt Whitman\n",
"John Kelly\n",
"Grace Lee\n",
"Nancy Grace\n",
"Garnet\n",
"Prince\n",
"Prince Felder\n",
"Prince Harry\n",
"Harry Styles\n",
"John Dean\n",
"Benjamin Franklin\n",
"Harrold Lloyd\n",
"Harrold Ford\n",
"Betty White\n",
"Meg Whitman\n",
"Christine Todd Whitman\n",
"Megyn Kelly\n",
"Grace Kelly\n",
"Grace Jones\n",
"Jack Nicholson\n",
"Jack Ruby\n",
"Jack Russel\n",
"Harry Fielder\n",
"Harry Trueman\n",
"Harry Jon Benjamin\n",
"John Edward\n",
"Benjamin Harrison\n",
"Harrison Ford\n",
"Henry Ford\n",
"Betty Ford\n",
"Betty Friedan\n",
"Chris Christie\n",
"Chris Pratt\n",
"Maggie Grace\n",
"Grace Hopper\n",
"Russel Crowe\n",
"Russ Smith\n",
"John Smith\n",
"Justin Long\n",
"John Bel Edwards\n",
"John Candy\n",
"John Henry\n",
"Henry James\n",
"Bill James\n",
"Chris Cooper\n",
"Chris Hemsworth\n",
"Chris Evans\n",
"Topher Grace\n",
"Van Morrison\n",
"Sheryl Crow\n",
"Sheryl Sandberg\n",
"Cameron Crow\n",
"Long John Silver\n",
"Olivia Newton John\n",
"Huey long\n",
"John Edwards\n",
"Candy Crowley\n",
"Aleister Crowley\n",
"James Fenimore Cooper\n",
"James Cook\n",
"Robert Frost\n",
"Bob Evans\n",
"Evan Tayler Jones\n",
"James Cameron\n",
"Cam Newton\n",
"Cameron Diaz\n",
"Huey Newton\n",
"Huey Lewis\n",
"John Lewis\n",
"Jenny Lewis\n",
"Ryan Lewis\n",
"Burt Reynolds\n",
"Alistair Cooke\n",
"Alistair Cookie\n",
"Cokie Roberts\n",
"John Roberts\n",
"Robert Johnson\n",
"Robert E. Lee\n",
"Tommy Lee\n",
"Tommy Lee Jones\n",
"Etta James\n",
"John Oliver\n",
"Ryan Reynolds\n",
"Alastair Reynolds\n",
"\"\"\")\n",
"\n",
"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)`)."
"len(tiles1970)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Approximate and Partial Matches\n",
"\n",
"The halves of two tiles match if they are an exact match, like \"Amy\" and \"Amy\", but also if they are an **approximate match**, like \"Amy\" and \"Aimee\". You can manually allow for this with:\n",
"\n",
" synsets = synonyms(\"Amy=Aimee, ...\")\n",
"\n",
"Another issue is a **partial match**: \"John Smith\" is allowed to match \"Long John Silver\"\n",
"because the first name \"John\" is a piece of the first name \"Long John\". allowed to match. If you pass in a list of tiles as a second argument to `synsets`, then all the pieces of a first name (if it has more than one piece) will be (individually) made synonyms of the whole first name.\n",
"\n",
"I also update `legal_match` to consult the `synsets` global variable for an approximate or partial match, and to handle a special case: a single names like \"Prince\" gets represented as the tile `('', 'Prince')`, but we don't want the empty first name of this tile to match the empty first name of \"Drake\", so we disallow an empty-to-empty match."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import collections\n",
"\n",
"Synset = set\n",
"\n",
"def synonyms(text, tiles=()): \n",
" \"synonyms('a=A, b=B') => dict(a={'a', 'A'}, A={'a', 'A'}, b={'b', 'B'}, B={'b', 'B'}\"\n",
" synsets = collections.defaultdict(set)\n",
" # Process `text`\n",
" for text1 in text.split(','):\n",
" synset = Synset(text1.strip().split('='))\n",
" for s in synset:\n",
" synsets[s] |= synset\n",
" # Process `tiles`\n",
" for (first, last) in tiles:\n",
" if ' ' in first:\n",
" for piece in first.split():\n",
" synsets[piece].add(first)\n",
" synsets[first].add(piece)\n",
" return synsets\n",
"\n",
"synsets = synonyms(\"\"\"\n",
" Amy=Aimee, Cook=Cooke=Cookie=Cokie, Alastair=Alistair, Columbo=Columbus, \n",
" Safire=Sapphire=Garnet, Garnet=Ruby, Charlie=Charles, Sean=Shawn, \n",
" Jimmy=James, Man=Mann, Jack=John, Tom=Tommy, Will=William=Williams, \n",
" Robert=Roberts=Bob, Cam=Cameron, Oliver=Olivia, Edward=Edwards, \n",
" Rich=Richard, Oscar=Oscar, Frank=Frank, Chris=Christopher=Topher,\n",
" Fat=Fats=Fatty\n",
" \"\"\", tiles1970)\n",
"\n",
"def legal_match(value, nbrval):\n",
" \"Does this value match the neighbor's value?\"\n",
" if value == '' == nbrval: return False\n",
" return (nbrval == value or nbrval is empty or nbrval is border or\n",
" nbrval in synsets[value])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Random Restart\n",
"\n",
"With many tiles, the program can get stuck before it has placed enough tiles. I could modify the program to *back up* in this case, but it will be easier to just *give up* and start again with an empty board. I can repeat this multiple times, and take the board on which the most tiles were placed. The function `best_dominoes` does this, and optionally plots the result. (I could fit more tiles on a larger square (with smaller tiles), but then the text would overflow the tiles even more than now.)"
]
},
{
@@ -312,72 +639,19 @@
"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": [
"110 / 126 tiles placed\n"
"110 / 270 tiles placed\n"
]
},
{
"data": {
"image/png": "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 truncated
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x11af01320>"
"<matplotlib.figure.Figure at 0x1158ec7b8>"
]
},
"metadata": {},
@@ -385,7 +659,16 @@
}
],
"source": [
"best_dominoes(tiles2);"
"def best_dominoes(tiles, n=20, figsize=16, plot=True, repeat=30):\n",
" board = max((dominoes(tiles, n) for _ in range(repeat)), \n",
" key=lambda board: len(board.boxes))\n",
" if plot:\n",
" print(len(board.boxes), '/', len(tiles), 'tiles placed')\n",
" plot_board(board, figsize)\n",
" else:\n",
" return board\n",
"\n",
"best_dominoes(tiles1970)"
]
}
],