{"id":450,"date":"2008-10-09T00:00:00","date_gmt":"2008-10-09T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/the-bejeweled-puzzle-in-sql\/"},"modified":"2021-09-29T16:22:09","modified_gmt":"2021-09-29T16:22:09","slug":"the-bejeweled-puzzle-in-sql","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/the-bejeweled-puzzle-in-sql\/","title":{"rendered":"The Bejeweled Puzzle in SQL"},"content":{"rendered":"<div id=\"pretty\">\n<p class=\"start\">Many DBAs and developers, at least those that have their servers and applications well under control, may be familiar with the popular, and rather addictive, PC and Xbox games such as Bejeweled and Jewel Quest. They belong to the group of &#8216;match 3&#8217; puzzles that are played on grids of different sizes, though typically 8&#215;8. The grid cells are filled with tokens, such as gems or coins, of various shapes and colours and the basic idea is to create horizontal or vertical rows containing a minimum of three matching tokens, by swapping adjacent tokens in the grid. For example, in the &#8216;bejewelled&#8217; puzzle shown in Figure 1, we can create a sequence of three blue stones in the second row of the grid by swapping the highlighted red stone with the blue one directly below it:<\/p>\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" height=\"459\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/580-image001.jpg\" width=\"630\" alt=\"580-image001.jpg\" \/><br \/><b>Figure 1. <\/b>Bejeweled puzzle<\/p>\n<p>All swaps must be done vertically or horizontally and only swaps that create a sequence of at least three tokens of the same kind, in a column or row, are permitted. When a successful swap is made, the sequence of collected tokens disappears, creating holes (gaps) in the grid. The tokens above these holes immediately cascade down to fill these holes, in turn creating holes in the&#160; top cells in the corresponding columns, which are filled by randomly-generated new tokens. The more tokens a player can make disappear in a single swap, the more points the player gets and the faster he can advance to the next level.<\/p>\n<p>After each swap, the game engine assesses the situation on the game board and if no further valid moves can be made, the game is over. In timed variants, the game can be also terminated if time expires. If valid moves exist, but a player does not make it during a certain time, or if a player presses the &#8220;Hint&#8221; button, the game engine shows the move that can be made.<\/p>\n<h2>The SQL Puzzle<\/h2>\n<p>It occurred to me that it would be a very interesting challenge to recreate a bejewled-style puzzle and solve it using SQL!<\/p>\n<p>Figure 2 shows a table, called <b>bejewelled<\/b>, which represents 9&#215;9 Bejeweled grid:<\/p>\n<table class=\"MsoTableGrid\" id=\"table1\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>#<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>1<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>8<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>7<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>6<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>9<\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>Figure 2.<\/b> Table bejeweled<\/p>\n<p>The blue numbers from 1 to 9 symbolize nine different types of tokens. In this case, we are dealing only with a fixed grid, rather than one that continually adds randomly-generated tokens, but the goal is similar: you must swap adjacent tokens to create an island of three or more identical numbers in a row or column. For example, if you swap the values of columns 1 and 2 in row 1, you will create a sequence of three 8&#8217;s in column 2 (rows 1, 2 and 3).<\/p>\n<h2>The Challenge!<\/h2>\n<p>So, here was the challenge! Using SQL or Transact-SQL, you needed to find <b>all of the possible moves <\/b>(horizontal and vertical) for the snapshot of Bejeweled table shown in Figure 2.<\/p>\n<p>You&#160;had to &#160;present your solution in the following format:<\/p>\n<table class=\"MsoTableGrid\" id=\"table2\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>Row number<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Column number<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Value<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>8<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>5<\/p>\n<\/td>\n<td valign=\"top\">\n<p>4<\/p>\n<\/td>\n<td valign=\"top\">\n<p>9<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Etc&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You did not need to show <b>where<\/b> to move the value of the specific cell. Just show <b>what<\/b> value to move, and from <b>what<\/b> cell it should be moved.<\/p>\n<p>There&#160;were no constraints on how you coded the solution: you could use any SQL and Transact-SQL statements, commands and build-in functions. SQL Server version-specific solutions (for instance, for SQL Server 2008 only) were also allowed. However you did it, you&#160;had to&#160;present your SQL code, along with a brief explanation of algorithm \/ implementation you used.<\/p>\n<p>The script in Listing 1&#160; allowed you to create and load table bejeweled:<\/p>\n<p><b>Listing1.<\/b> Create and load table bejewelled<\/p>\n<pre>IF EXISTS(SELECT * FROM sysobjects&#160;\n&#160;&#160; WHERE ID = (OBJECT_ID('bejeweled')) AND xtype = 'U')\nDROP TABLE bejeweled\nGO\n\nCREATE TABLE bejeweled(\n&#160;[#] [tinyint] NULL,\n&#160;[1] [tinyint] NULL,\n&#160;[2] [tinyint] NULL,\n&#160;[3] [tinyint] NULL,\n&#160;[4] [tinyint] NULL,\n&#160;[5] [tinyint] NULL,\n&#160;[6] [tinyint] NULL,\n&#160;[7] [tinyint] NULL,\n&#160;[8] [tinyint] NULL,\n&#160;[9] [tinyint] NULL\n)\n\nINSERT INTO bejeweled VALUES(1, 8, 5, 2, 8, 6, 8, 9, 9, 1);\nINSERT INTO bejeweled VALUES(2, 3, 8, 6, 4, 9, 3, 6, 9, 6);\nINSERT INTO bejeweled VALUES(3, 2 , 8, 9, 3, 4, 3, 7, 4, 9);\nINSERT INTO bejeweled VALUES(4, 1, 9, 4, 5, 7, 9, 9, 5, 6);\nINSERT INTO bejeweled VALUES(5, 7, 6, 2, 9, 7, 9, 9, 6, 8);\nINSERT INTO bejeweled VALUES(6, 5, 2, 9, 6, 5, 8, 6, 8, 9);\nINSERT INTO bejeweled VALUES(7, 4, 4, 2, 5, 9, 5, 8, 6, 9);\nINSERT INTO bejeweled VALUES(8, 5, 9, 9, 3, 7, 7, 6, 2, 6);\nINSERT INTO bejeweled VALUES(9, 2, 3, 2, 7, 9, 6, 6, 2, 9);\n<\/pre>\n<p>There&#160;were three winners:<\/p>\n<ul>\n<li><b>Timothy Walters<\/b> &#8211; for his second solution with the offset table; <i>He gets a $50 Amazon Voucher.<\/i>  <\/li>\n<li><b>Ryan Randall<\/b> &#8211; for his first and second solutions;<i> He gets a choice between a license for SQL Prompt and SQL Data Generator<\/i>  <\/li>\n<li><b>DrLechter<\/b> &#8211; for his first and second solutions; <i>He gets a choice between a license for SQL Prompt and SQL Data Generator<\/i> <\/li>\n<\/ul>\n<p>Timothy Walters&#8217;s solution is very simple and elegant. Almost all of the logic has been implemented in the lookup table and that makes the solution portable and flexible.<\/p>\n<p>In addition, his solution can be easiely modified in order to show the directions of the moves by adding one more column to the offset table. <\/p>\n<p>Here is how it can be done: <\/p>\n<p><code><br \/>--====== Table matches needs to be loaded only once<br \/>CREATE TABLE matches(offsetRow1 INT, offsetCol1 INT, offsetRow2 INT, ofsetCol2 INT, directions VARCHAR(20))<br \/>-- for horizontal <br \/>INSERT INTO matches VALUES(-1, -1, -1, -2, 'up')<br \/>INSERT INTO matches VALUES(-1, -1, -1, 1, 'up')<br \/>INSERT INTO matches VALUES(-1, 1, -1, 2, 'up')&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br \/>INSERT INTO matches VALUES( 1, -1, 1, -2, 'down')&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>INSERT INTO matches VALUES( 1, -1, 1, 1, 'down')<br \/>INSERT INTO matches VALUES( 1, 1, 1, 2, 'down')&#160;&#160;&#160;&#160;&#160;&#160; <br \/>INSERT INTO matches VALUES( 0, -2, 0, -3, 'left')&#160;&#160;&#160;&#160; <br \/>INSERT INTO matches VALUES( 0, 2, 0, 3, 'right')&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br \/>-- for verical<br \/>INSERT INTO matches VALUES(-2, -1, -1, -1, 'left')<br \/>INSERT INTO matches VALUES(-1, -1, 1, -1, 'left')<br \/>INSERT INTO matches VALUES( 1, -1, 2, -1, 'left')<br \/>INSERT INTO matches VALUES(-2, 1, -1, 1, 'right')<br \/>INSERT INTO matches VALUES(-1, 1, 1, 1, 'right')<br \/>INSERT INTO matches VALUES( 1, 1, 2, 1, 'right')<br \/>INSERT INTO matches VALUES(-2, 0, -3, 0, 'up')<br \/>INSERT INTO matches VALUES( 2, 0, 3, 0, 'down')<\/p>\n<p>--==================================================<br \/>;WITH CTE<br \/>&#160;&#160;&#160;&#160;&#160;&#160;AS<br \/>&#160;&#160;(<br \/>&#160;&#160;SELECT<br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[Row] = CAST( [#] AS INT ),<br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[Col] = CAST( [Col] AS INT ),<br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[Value]<br \/>&#160;&#160;&#160;&#160;FROM bejeweled<br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;UNPIVOT ([Value] FOR [Col] IN ([1],[2],[3],[4],[5],[6],[7],[8],[9])) unpvt<br \/>&#160;&#160;)<br \/>SELECT DISTINCT T.Row, T.Col, T.Value, directions<br \/>&#160;&#160;FROM CTE T<br \/>&#160;&#160;&#160;&#160;&#160;&#160;JOIN CTE T1<br \/>&#160;&#160;&#160;&#160;&#160;&#160;ON T.Value = T1.Value<br \/>&#160;&#160;&#160;&#160;&#160;&#160;JOIN CTE T2<br \/>&#160;&#160;&#160;&#160;&#160;&#160;ON T.Value = T2.Value<br \/>&#160;&#160;&#160;&#160;&#160;&#160;JOIN matches<br \/>&#160;&#160;&#160;&#160;&#160;&#160;ON (T1.Row - T.Row) = offsetRow1<br \/>&#160;&#160;&#160;&#160;AND (T1.Col - T.Col) = offsetCol1<br \/>&#160;&#160;&#160;&#160;AND (T2.Row - T.Row) = offsetRow2<br \/>&#160;&#160;&#160;&#160;AND (T2.Col - T.Col) = ofsetCol2<br \/>&#160;&#160;ORDER BY T.Row, T.Col<br \/><\/code> <\/p>\n<p>There is one more person &#8211; AmandaS that should be specially mentioned. Her second solution was very similar to Timothy Walters&#8217; and Ryan Randall&#8217;s solutions, but was submitted five days later.<\/p>\n<p>A few more people should be mentioned as well:<\/p>\n<ul>\n<li><b>Saharafrog <\/b>&#8211; interesting solution  <\/li>\n<li><b>Doug Stewart <\/b> <\/li>\n<li><b>Steve Munson<\/b> &#8211; his solution shows the coordinates of the cells that should be moved and the coordinates of destination cells  <\/li>\n<li><b>RBarry Young <\/b> <\/li>\n<li><b>Dennis A<\/b>  <\/li>\n<li><b>SkyBeaver <\/b><\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Alex Kozak provides another SQL puzzle to hone your SQL Skills with.  &hellip;<\/p>\n","protected":false},"author":221828,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143531],"tags":[4150,4252],"coauthors":[],"class_list":["post-450","post","type-post","status-publish","format-standard","hentry","category-t-sql-programming-sql-server","tag-sql","tag-t-sql-programming"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/450","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221828"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=450"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/450\/revisions"}],"predecessor-version":[{"id":92560,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/450\/revisions\/92560"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=450"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}