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