Guest User

SQL Server Minesweeper

a guest
Dec 4th, 2012
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
T-SQL 7.42 KB | None | 0 0
  1. Copied from here: http:Q_26487697.html#a33724604 (I own the post)
  2.  
  3. -- the most basic component, the Square.
  4. if object_id('Square') is not null
  5. drop table Square
  6. GO
  7. create table Square(
  8.     row int,
  9.     col int,
  10.     mine bit default(0), -- this square is a mine
  11.     neighbouring_mines tinyint, -- how many neighbours are mines
  12.     opened bit default(0), -- 1 for opened
  13.     flagged bit default(0),
  14.     suspected bit default(0),
  15.     primary key clustered(row,col) -- good for column travel given row
  16. );
  17. create index ix_Square_colrow on Square(col,row); -- travel by row given column
  18. create index ix_Square_opened on Square(opened,mine); -- game ending test
  19.  
  20. -- very basic procedure for showing the mine field
  21. -- @what is very flexible (and open to SQL injection, but who cares)
  22. if object_id('ShowMineField') is not null
  23. drop proc ShowMineField
  24. GO
  25. create proc ShowMineField
  26. @what varchar(1000)
  27. as
  28. declare @sql nvarchar(max)
  29. declare @cols nvarchar(max)
  30. select @cols = coalesce(@cols+',','') + '['+convert(varchar(10),col)+']'
  31. from (select distinct col from Square) M
  32. set @sql = '
  33. select ' + @cols + '
  34. from (select row,col,what=' + @what + ' from Square) p
  35. pivot (max(what) for col in (' + @cols + ')) pv
  36. order by row'
  37. --print (@sql)
  38. exec (@sql)
  39. GO
  40.  
  41. -- shows the game view of the mine field
  42. if object_id('ShowMineField_Game') is not null
  43. drop proc ShowMineField_Game
  44. GO
  45. create proc ShowMineField_Game
  46. AS
  47. exec ShowMineField 'case
  48.     when opened&mine=1 then ''X''
  49.     when opened=1 then coalesce(convert(char(1),nullif(neighbouring_mines,0)),'''')
  50.     when flagged=1 then ''(B)''
  51.     when suspected=1 then ''(?)''
  52.     else ''<>'' end'
  53. GO
  54.  
  55. -- shows where the mines are
  56. if object_id('ShowMineField_Mines') is not null
  57. drop proc ShowMineField_Mines
  58. GO
  59. create proc ShowMineField_Mines
  60. AS
  61. -- uncomment this line to CHEAT. or DEBUG
  62. --exec ShowMineField 'convert(char(1),mine) + ''/'' + convert(char(1),neighbouring_mines)'
  63. GO
  64.  
  65. -- shows all details about all square
  66. if object_id('ShowMineField_Info') is not null
  67. drop proc ShowMineField_Info
  68. GO
  69. create proc ShowMineField_Info
  70. AS
  71. -- uncomment this line to CHEAT. or DEBUG
  72. --exec ShowMineField '
  73. --  convert(char(1),mine) +
  74. --  convert(char(1),neighbouring_mines) +
  75. --  convert(char(1),opened) +
  76. --  convert(char(1),flagged) +
  77. --  convert(char(1),suspected)'
  78. GO
  79.  
  80. -- check to see if the game has ended
  81. if object_id('GameEnded') is not null
  82. drop function GameEnded
  83. GO
  84. create function GameEnded() returns varchar(20) as
  85. begin
  86. if exists (select * from Square where opened&mine=1)
  87.     return '** You lost'
  88. if (select count(*)-sum(1*opened)-sum(1*mine) from Square) = 0
  89.     return '** You won'
  90. return null
  91. end
  92. GO
  93.  
  94. -- shows all details about all square
  95. if object_id('ShowGameStatus') is not null
  96. drop proc ShowGameStatus
  97. GO
  98. create proc ShowGameStatus
  99. @message varchar(100) = ''
  100. AS
  101. if dbo.gameEnded()>'' set @message = dbo.gameEnded()
  102. select
  103.     Opened=sum(1*opened),
  104.     Flagged=sum(1*flagged),
  105.     [Total Mines]=sum(1*mine),
  106.     [Total Squares]=count(*),
  107.     [Message]=@message
  108. from Square
  109. exec ShowMineField_Game
  110. exec ShowMineField_Mines
  111. exec ShowMineField_Info
  112. GO
  113.  
  114. -- function to generate a new mine field
  115. if object_id('GenerateMineField') is not null
  116. drop proc GenerateMineField
  117. GO
  118. create proc GenerateMineField
  119. @rows int,
  120. @cols int,
  121. @mines int
  122. AS
  123. -- clear the current field
  124. truncate table Square;
  125. -- generate entire field including mines
  126. ;with generator as (
  127.     select row=r.number, col=c.number, rn = row_number() over (order by newid())
  128.     from master..spt_values r
  129.     inner join master..spt_values c on c.type='P' and c.number between 1 and @cols
  130.     where r.type='P' and r.number between 1 and @rows
  131. )
  132. insert Square (row, col, mine)
  133. select row, col, case when rn<=@mines then 1 else 0 end
  134. from generator;
  135.  
  136. -- one-off counting of neighbouring mines for each mine
  137. ;with mines as (
  138.     select a.row, a.col, c=count(*)
  139.     from Square a
  140.     inner join Square b on abs(a.row-b.row)<=1 and abs(a.col-b.col)<=1
  141.         and not (a.col=b.col and a.row=b.row)
  142.         and b.mine=1
  143.     group by a.row, a.col)
  144. update m
  145. set neighbouring_mines=coalesce(b.c,0)
  146. from Square m
  147. left join mines b on m.row=b.row and m.col=b.col
  148.  
  149. -- show game board
  150. exec ShowGameStatus 'Ready';
  151. GO
  152.  
  153. -- helper function to format a reference to a cell in RC notation
  154. if object_id('formatStatus') is not null
  155. drop function formatStatus
  156. GO
  157. create function formatStatus(@row int, @col int, @message varchar(100)) returns varchar(50) as
  158. begin
  159. return ('R' + convert(varchar(10),@row) + 'C' + convert(varchar(10),@col) + ': ' + @message)
  160. end
  161. GO
  162.  
  163. -- this toggles a cell between unmarked, safe or suspected
  164. if object_id('MarkCell') is not null
  165. drop proc MarkCell
  166. GO
  167. create proc MarkCell @row int, @col int AS
  168. if dbo.gameEnded()>'' begin exec ShowGameStatus; return; end;
  169.  
  170. update Square set
  171.     flagged = case when flagged|suspected=1 then 0 else 1 end,
  172.     suspected = case when flagged=1 then 1 else 0 end
  173. where row=@row and col=@col and opened=0
  174. if @@rowcount=0
  175. begin
  176.     declare @status varchar(100)
  177.     set @status = dbo.formatStatus(@row,@col,'is not valid for marking')
  178.     exec ShowGameStatus @status
  179. end
  180. else
  181.     exec ShowGameStatus;
  182. GO
  183. if exists (select * from sys.synonyms where name='m')
  184. drop synonym m;
  185. create synonym m for MarkCell
  186. GO
  187.  
  188. -- this opens up a cells next to already opened cells, that are
  189. -- next to opened cells with 0 neighbouring mines
  190. if object_id('OpenZeroCounters') is not null
  191. drop proc OpenZeroCounters
  192. GO
  193. create proc OpenZeroCounters AS
  194. set nocount on;
  195. select a=1 into #tmp;
  196. while @@rowcount>0
  197.     update a
  198.     set opened=1, flagged=0, suspected=0
  199.     from Square a
  200.     inner join Square b on abs(a.row-b.row)<=1 and abs(a.col-b.col)<=1
  201.         and b.opened=1 and b.neighbouring_mines=0
  202.     where a.opened=0
  203. exec ShowGameStatus;
  204. GO
  205.  
  206. -- this opens up a cell to reveal the big question: mine or no mine
  207. if object_id('OpenCell') is not null
  208. drop proc OpenCell
  209. GO
  210. create proc OpenCell @row int, @col int AS
  211. if dbo.gameEnded()>'' begin exec ShowGameStatus; return; end;
  212.  
  213. update Square
  214.     set opened=1, flagged=0, suspected=0
  215. where row=@row and col=@col and opened=0
  216. if @@rowcount=0
  217. begin
  218.     declare @status varchar(100)
  219.     set @status = dbo.formatStatus(@row,@col,'is not valid for opening')
  220.     exec ShowGameStatus @status
  221. end
  222. else
  223.     exec OpenZeroCounters
  224. GO
  225. if exists (select * from sys.synonyms where name='o')
  226. drop synonym o;
  227. create synonym o for OpenCell
  228. GO
  229.  
  230. -- if there are as many neighbouring mines as flagged,
  231. -- we take a big step and open up all unflagged neighbours
  232. if object_id('QuickClear') is not null
  233. drop proc QuickClear
  234. GO
  235. create proc QuickClear @row int, @col int AS
  236. if dbo.gameEnded()>'' begin exec ShowGameStatus; return; end;
  237.  
  238. update c
  239. set opened=1
  240. from Square a
  241. inner join Square c on abs(a.row-c.row)<=1 and abs(a.col-c.col)<=1 and c.flagged=0
  242. where a.row=@row and a.col=@col
  243.     and a.opened=1 -- only on opened cells, I think this is right(?)
  244.     and a.neighbouring_mines = (
  245.         select count(*)
  246.         from Square b
  247.         where abs(a.row-b.row)<=1 and abs(a.col-b.col)<=1
  248.         and b.flagged=1)
  249. if @@rowcount=0
  250. begin
  251.     declare @status varchar(100)
  252.     set @status = dbo.formatStatus(@row,@col,'is not valid for clearing')
  253.     exec ShowGameStatus @status
  254. end
  255. else
  256.     exec OpenZeroCounters
  257. GO
  258. if exists (select * from sys.synonyms where name='c')
  259. drop synonym c;
  260. create synonym c for QuickClear
  261. GO
  262.  
  263. exec GenerateMineField 50,50,10; -- this and open a single took 21s to clear
  264. exec m 10, 5;
  265. exec o 10, 1;
  266. exec c 9, 4;
Advertisement
Add Comment
Please, Sign In to add comment