-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.sql
More file actions
889 lines (786 loc) · 28.6 KB
/
Copy pathsetup.sql
File metadata and controls
889 lines (786 loc) · 28.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
-- ########################################
-- SET UP THE GAME TABLES & STARTING STATES
-- ########################################
-- minefield → defines actual bombs placement & map
DROP TABLE IF EXISTS minefield;
CREATE TABLE IF NOT EXISTS minefield(
row_id SERIAL PRIMARY KEY,
"A" VARCHAR(40) DEFAULT 0,
"B" VARCHAR(40) DEFAULT 0,
"C" VARCHAR(40) DEFAULT 0,
"D" VARCHAR(40) DEFAULT 0,
"E" VARCHAR(40) DEFAULT 0,
"F" VARCHAR(40) DEFAULT 0,
"G" VARCHAR(40) DEFAULT 0,
"H" VARCHAR(40) DEFAULT 0,
"I" VARCHAR(40) DEFAULT 0,
"J" VARCHAR(40) DEFAULT 0,
"K" VARCHAR(40) DEFAULT 0,
"L" VARCHAR(40) DEFAULT 0,
"M" VARCHAR(40) DEFAULT 0,
"N" VARCHAR(40) DEFAULT 0,
"O" VARCHAR(40) DEFAULT 0,
"P" VARCHAR(40) DEFAULT 0
);
-- contains all 40 mines & their cords
DROP TABLE IF EXISTS mine_table;
CREATE TABLE IF NOT EXISTS mine_table(
mine_id INTEGER,
x INTEGER,
y INTEGER
);
-- user display is what is returned to the user & what they see
DROP TABLE IF EXISTS user_display;
CREATE TABLE IF NOT EXISTS user_display(
row_id SERIAL PRIMARY KEY,
"A" VARCHAR(40) DEFAULT '[ - ]',
"B" VARCHAR(40) DEFAULT '[ - ]',
"C" VARCHAR(40) DEFAULT '[ - ]',
"D" VARCHAR(40) DEFAULT '[ - ]',
"E" VARCHAR(40) DEFAULT '[ - ]',
"F" VARCHAR(40) DEFAULT '[ - ]',
"G" VARCHAR(40) DEFAULT '[ - ]',
"H" VARCHAR(40) DEFAULT '[ - ]',
"I" VARCHAR(40) DEFAULT '[ - ]',
"J" VARCHAR(40) DEFAULT '[ - ]',
"K" VARCHAR(40) DEFAULT '[ - ]',
"L" VARCHAR(40) DEFAULT '[ - ]',
"M" VARCHAR(40) DEFAULT '[ - ]',
"N" VARCHAR(40) DEFAULT '[ - ]',
"O" VARCHAR(40) DEFAULT '[ - ]',
"P" VARCHAR(40) DEFAULT '[ - ]'
);
-- track the user's position
DROP TABLE IF EXISTS user_position;
CREATE TABLE IF NOT EXISTS user_position(
id SERIAL PRIMARY KEY,
positionX INTEGER NOT NULL,
positionY INTEGER NOT NULL
);
-- track the user's last action
DROP TABLE IF EXISTS user_action;
CREATE TABLE IF NOT EXISTS user_action(
id SERIAL PRIMARY KEY,
positionX INTEGER NOT NULL,
positionY INTEGER NOT NULL,
action_type VARCHAR(40) NOT NULL
);
-- tracks all flags user places
DROP TABLE IF EXISTS flags;
CREATE TABLE IF NOT EXISTS flags(
row_id SERIAL PRIMARY KEY,
positionX INTEGER NOT NULL,
positionY INTEGER NOT NULL
);
-- tracks what the tile under the user's current position is
-- this is used to ensure that once the user leaves that tile, the state is maintained
DROP TABLE IF EXISTS prev_tile;
CREATE TABLE IF NOT EXISTS prev_tile(
row_id SERIAL PRIMARY KEY,
prev VARCHAR(40)
);
-- used in recursive function during zero-open
-- cleared after zero open
DROP TABLE IF EXISTS visited;
CREATE TABLE IF NOT EXISTS visited(
id SERIAL PRIMARY KEY,
positionX INTEGER NOT NULL,
positionY INTEGER NOT NULL
);
-- tracks how many tiles are revealed
DROP TABLE IF EXISTS revealed;
CREATE TABLE IF NOT EXISTS revealed(
id SERIAL PRIMARY KEY,
revealedTiles INTEGER NOT NULL
);
-- INSERT & BEGINNING STAGE OF TABLES
INSERT INTO minefield("A") VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0), (0);
INSERT INTO user_display("A") VALUES ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]'), ('[ - ]');
INSERT INTO user_action (positionX, positionY, action_type) VALUES (0,0,'N');
INSERT INTO prev_tile(prev) VALUES('[ - ]');
-- ########################################
-- ########### SET UP FUNCTIONS ###########
-- ########################################
-- ### FUNCTION: generate 40 unique mines
-- x_cord = stores the x-cordinate
-- y_cord = stores the y-cordinate
-- duplicate = boolean value, acts as a flag to indicate repeated bombs
-- total_bombs = counts bombs & condition to stop loop
CREATE OR REPLACE FUNCTION generate_mines()
RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
x_cord INTEGER := 0;
y_cord INTEGER := 0;
duplicate INTEGER := 0;
total_bombs INTEGER := 1;
BEGIN
WHILE total_bombs < 41 LOOP
-- generates random value from 1 to 16
y_cord = floor(random() * 16) + 1;
x_cord = floor(random() * 16) + 1;
-- selects the number of rows (0 or 1) that have the same cords
-- as the generated values
EXECUTE format('
SELECT COUNT(*)
FROM mine_table mt
WHERE mt.x = $1 AND mt.y = $2
')
INTO duplicate
USING x_cord, y_cord;
-- if duplicate is false
IF duplicate = 0 THEN
-- insert the new bomb into the mine table
INSERT INTO mine_table (mine_id, x, y)
VALUES(total_bombs, x_cord, y_cord);
-- iterate the bomb - if there's a duplicate bomb, this never gets iterated
total_bombs = total_bombs + 1;
END IF;
END LOOP;
END;
$$;
SELECT * FROM generate_mines();
-- ### FUNCTION: inserts the bombs into the mine_table
-- col_char = stored the character converted x-cord
-- x_cord = stores the x-cordinate
-- y_cord = stores the y-cordinate
CREATE OR REPLACE FUNCTION insert_bombs()
RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
col_char CHAR(1);
y_cord INTEGER := 0;
x_cord INTEGER := 0;
BEGIN
FOR i IN 1..40 LOOP
-- select the y-cord & x-cord from the mine_table
SELECT mt.y, mt.x
INTO y_cord, x_cord
FROM mine_table mt
WHERE mt.mine_id = i;
-- convert the x_cord into a character
col_char := CHR(64 + x_cord);
-- update the mine_field using the character & y-cord
EXECUTE format('
UPDATE minefield mf
SET %I = $1
WHERE mf.row_id = $2
', col_char) USING 'M', y_cord;
END LOOP;
END;
$$;
SELECT * FROM insert_bombs();
-- ### FUNCTION: counts the adjacent bombs beside each cell
-- total_current_cell_value = stores the number of bombs near cell
-- col_char = stored the character converted x-cord
-- is_bomb = flag on whether the cell contains a bomb
CREATE OR REPLACE FUNCTION count_adjacent_bombs(x_cord int, y_cord int)
RETURNS INTEGER
LANGUAGE plpgsql AS $$
DECLARE
total_current_cell_value INTEGER := 0;
is_bomb INTEGER := 0;
col_char CHAR(1);
BEGIN
FOR i IN -1..1 LOOP -- check from top left
FOR j IN -1..1 LOOP -- to bottom right
IF i = 0 AND j = 0 THEN -- skip the center cell
ELSE
-- handles the edge cases so that if the cell is on the border, it skips the out of bounds columns / rows
IF x_cord = 1 AND j = -1 OR x_cord = 16 AND j = 1 OR y_cord = 1 AND i = -1 OR y_cord = 16 AND i = 1 THEN
ELSE
-- convert the x_cord into a character
col_char := CHR(64 + x_cord + j);
-- checks if the cell is equal to 'M'
-- set 1 if there's a bomb, otherwise 0
EXECUTE format('
SELECT CASE WHEN %I = $1 THEN 1 ELSE 0 END
FROM minefield
WHERE row_id = $2
', col_char)
INTO is_bomb
USING 'M', y_cord + i;
-- add bombs to the total_current_cell_value
total_current_cell_value := total_current_cell_value + is_bomb;
END IF;
END IF;
END LOOP;
END LOOP;
-- returns the total_current_cell_valued
RETURN total_current_cell_value;
END;
$$;
-- ### FUNCTION: initiates the count of adjacent bombs
-- col_char = stored the character converted x-cord
-- adj_bombs = stores the adjacent bombs
CREATE OR REPLACE FUNCTION initial_count()
RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
col_char CHAR(1);
adj_bombs INTEGER := 0;
BEGIN
-- for all 16 rows & columns
FOR y_cord in 1..16 LOOP
FOR x_cord in 1..16 LOOP
-- convert the x_cord into a character
col_char := CHR(64 + x_cord);
-- count the adjacent bombs
SELECT * FROM count_adjacent_bombs(x_cord, y_cord)
INTO adj_bombs;
-- update the value of that cell
EXECUTE format('
UPDATE minefield mf
SET %I = $1
WHERE mf.row_id = $2 AND mf.%I != $3',
col_char, col_char) USING adj_bombs, y_cord, 'M';
END LOOP;
END LOOP;
END;
$$;
SELECT initial_count();
-- ### FUNCTION: find the first cell that's has 0 bombs
-- col_char = stored the character converted x-oord
-- current_cell_value = stores the bomb count of a cell
-- x_cord = tracks the x_cord
-- y_cord = tracks the y_cord
CREATE OR REPLACE FUNCTION startingPoint()
RETURNS VOID AS $$
DECLARE
col_char VARCHAR(1) := '';
current_cell_value VARCHAR(1) := 0;
x_cord INTEGER := 1;
y_cord INTEGER := 1;
BEGIN
LOOP
LOOP
-- convert the x_cord into a character
col_char := CHR(64 + x_cord);
-- find the current_cell_value of the current cell
-- starts at (1,1)
EXECUTE format('
SELECT mf.%I
FROM minefield mf
WHERE row_id = $1', col_char)
INTO current_cell_value
USING y_cord;
-- if the current_cell_value is 0
IF current_cell_value = '0' THEN
-- insert the starting position
INSERT INTO user_position (positionX, positionY) VALUES (x_cord, y_cord);
-- sets x_cord and y_cord to meet end conditions
x_cord := 15;
y_cord := 15;
END IF;
-- iterates the x-cord
x_cord := x_cord + 1;
-- end condition
EXIT WHEN x_cord = 16;
END LOOP;
-- reset the col
x_cord := 1;
-- iterate the y-cord → starts at 1
y_cord := y_cord + 1;
-- end condition
EXIT WHEN y_cord = 16;
END LOOP;
END
$$ LANGUAGE plpgsql;
SELECT startingPoint();
-- ########################################
-- ############ USER CONTROLS #############
-- ########################################
CREATE OR REPLACE FUNCTION move_up() RETURNS VOID AS $$
BEGIN
UPDATE user_position
SET positionY = positionY - 1
WHERE id = 1 AND positionY != 1;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION move_down() RETURNS VOID AS $$
BEGIN
UPDATE user_position
SET positionY = positionY + 1
WHERE id = 1 AND positionY != 16;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION move_left() RETURNS VOID AS $$
BEGIN
UPDATE user_position
SET positionX = positionX - 1
WHERE id = 1 AND positionX != 1;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION move_right() RETURNS VOID AS $$
BEGIN
UPDATE user_position
SET positionX = positionX + 1
WHERE id = 1 AND positionX != 16;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION flag() RETURNS VOID AS $$
DECLARE
x_cord INTEGER := 0;
y_cord INTEGER := 0;
BEGIN
SELECT up.positionY, up.positionX
INTO y_cord, x_cord
FROM user_position up
WHERE up.id = 1;
UPDATE user_action
SET positionY = y_cord, positionX = x_cord, action_type = 'F'
WHERE id = 1;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mark() RETURNS VOID AS $$
DECLARE
x_cord INTEGER := 0;
y_cord INTEGER := 0;
BEGIN
SELECT up.positionY, up.positionX
INTO y_cord, x_cord
FROM user_position up
WHERE up.id = 1;
UPDATE user_action
SET positionY = y_cord, positionX = x_cord, action_type = 'R'
WHERE id = 1;
END;
$$ LANGUAGE plpgsql;
-- ########################################
-- ############## GAME LOOP ###############
-- ########################################
-- Game loop consists of 3 main functions called in this order:
-- 1. display_state() → shows the current state of the board
-- a. displays where the user is based on input
-- b. calls enter_action() to account if an action is input
-- 2. enter_action() → considers 2 actions, R or F
-- R → reveals a cell
-- F → flags a cell as a bomb
-- 3. clear_movement() → this function resets the user position to reveal the cell under it
-- ### FUNCTION: performs an action
-- col_char = stored the character converted x-cord
-- current_cell_value = tracks what the value in a cell is
-- in_flag = tracks which flags have been placed
-- update_value = tracks the current user-display value
CREATE OR REPLACE FUNCTION enter_action(x_cord int, y_cord int, uAct char) RETURNS VOID AS $$
DECLARE
col_char VARCHAR(1) := '';
current_cell_value VARCHAR(1) := '';
in_flag INTEGER := 0;
user_display_value VARCHAR(40) := '[ - ]';
BEGIN
-- convert the x_cord into a character
col_char := CHR(64 + x_cord);
-- checks the value in the current cell
EXECUTE format('
SELECT mf.%I
FROM minefield mf
WHERE row_id = $1', col_char)
INTO current_cell_value
USING y_cord;
-- check if the current cell has a record in the flag table
-- output: 0 → this cell has not been flagged
-- output: 1 → this cell has been flagged
SELECT count(*)
FROM flags f
WHERE f.positionX = x_cord AND f.positionY = y_cord
INTO in_flag;
-- find the previous value of the current cell before it's updated with X
SELECT pt.prev
INTO user_display_value
FROM prev_tile pt
WHERE pt.row_id = 1;
-- toggle flag
-- the user presses the F key
IF uAct = 'F' THEN
-- if the flag is not currently flagged
-- if the current displayed value is [ - ] = prevents flagging numbers
IF in_flag = 0 AND user_display_value = '[ - ]' THEN
-- add the flag into the tracker
INSERT INTO flags(positionX, positionY) VALUES(x_cord, y_cord);
END IF;
-- if the flag is currently flagged
IF in_flag = 1 THEN
-- remove the flag from the tracker
DELETE FROM flags WHERE positionX = x_cord AND positionY = y_cord;
END IF;
END IF;
-- if the user presses the R key
IF uAct = 'R' THEN
-- ## OPEN TO THE GAME
-- if the cell contains 0 mines & user tries to reveal
IF current_cell_value = '0' THEN
-- zero open & reveal nearest neighbour
PERFORM zero_open(x_cord, y_cord);
ELSE
-- ## USER REVEALS A MINE
-- if current cell contains a mine & user tries to reveal
IF current_cell_value = 'M' THEN
-- set action to 'L' which initiates the end
UPDATE user_action
SET action_type = 'L'
WHERE id = 1;
-- ## chording
-- if the user display is empty / not revealed (aka pressing R on any revealed cell)
ELSIF user_display_value != '[ - ]' THEN
-- perform chording
PERFORM chording(x_cord, y_cord);
END IF;
END IF;
END IF;
-- AFTER ALL ACTIONS ARE COMPLETE:
-- we update the user_display to show the marker
EXECUTE format('
UPDATE user_display ud
SET %I = $1
WHERE ud.row_id = $2
', col_char) USING '[ X ]', y_cord;
END;
$$ LANGUAGE plpgsql;
-- ### FUNCTION: chording function
-- col_char = stored the character converted x-cord
-- current_cell_value =
-- user_display_value =
-- update_value =
CREATE OR REPLACE FUNCTION chording(x_cord int, y_cord int) RETURNS VOID AS $$
DECLARE
col_char VARCHAR(1) := '';
current_cell_value VARCHAR(1) := '';
user_display_value VARCHAR(10) := '';
update_value VARCHAR(40) := '[ - ]';
BEGIN
FOR i IN -1..1 LOOP -- check from top left
FOR j IN -1..1 LOOP -- to bottom right
IF i = 0 AND j = 0 THEN -- skip the center cell
ELSE
-- skip out of bound indexes
IF x_cord = 1 AND j = -1 OR x_cord = 16 AND j = 1 OR y_cord = 1 AND i = -1 OR y_cord = 16 AND i = 1 THEN
ELSE
-- convert the x_cord into a character
col_char := CHR(64 + x_cord + j);
-- find the current cell value in the cell
EXECUTE format('
SELECT mf.%I
FROM minefield mf
WHERE mf.row_id = $1', col_char)
INTO current_cell_value
USING y_cord + i;
-- find the current value in the user_display for that cell
EXECUTE format('
SELECT %I
FROM user_display ud
WHERE row_id = $1
', col_char)
INTO user_display_value
USING y_cord + i;
-- if the cell is a bomb & it wasn't revealed (aka [ - ] value in user display)
IF current_cell_value = 'M' AND user_display_value = '[ - ]' THEN
-- user loses the game
UPDATE user_action
SET action_type = 'L'
WHERE id = 1;
-- if the cell is equal to 0, then we need to perform a zero-open
ELSIF current_cell_value = '0' THEN
PERFORM zero_open(x_cord, y_cord);
ELSE
-- otherwise, reveal all cells around the selected cell
EXECUTE format('
SELECT mf.%I
FROM minefield mf
WHERE mf.row_id = $1', col_char)
INTO current_cell_value
USING y_cord + i;
IF current_cell_value = 'M' THEN
update_value := '[ F ]';
ELSE
update_value := '[ ' || current_cell_value || ' ]';
END IF;
EXECUTE format('
UPDATE user_display ud
SET %I = $1
WHERE ud.row_id = $2', col_char)
USING update_value, y_cord + i;
END IF;
END IF;
END IF;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- ### FUNCTION: this function opens all surrounding cells when a 0 is revealed
-- col_char = stored the character converted x-cord
-- current_cell_value = tracks the value stored in the current cell
-- is_visited = tracks which cells have been visited
-- update_value = contains the value to be updated
CREATE OR REPLACE FUNCTION zero_open(x_cord int, y_cord int) RETURNS VOID AS $$
DECLARE
col_char VARCHAR(1) := '';
current_cell_value VARCHAR(1) := '';
is_visited INTEGER := 0;
update_value VARCHAR(40) := '[ - ]';
BEGIN
FOR i IN -1..1 LOOP -- check from top left
FOR j IN -1..1 LOOP -- to bottom right
IF i = 0 AND j = 0 THEN -- skip the center cell
ELSE
-- skip out of bound indexes
IF x_cord = 1 AND j = -1 OR x_cord = 16 AND j = 1 OR y_cord = 1 AND i = -1 OR y_cord = 16 AND i = 1 THEN
ELSE
-- convert the x_cord into a character
col_char := CHR(64 + x_cord + j);
-- find the current cell value in the cell
EXECUTE format('
SELECT mf.%I
FROM minefield mf
WHERE mf.row_id = $1', col_char)
INTO current_cell_value
USING y_cord + i;
EXECUTE format('
UPDATE user_display ud
SET %I = CASE
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
WHEN $1 = %L THEN %L
ELSE %I
END
WHERE ud.row_id = $2',
col_char,
'0', '[ 0 ]',
'1', '[ 1 ]',
'2', '[ 2 ]',
'3', '[ 3 ]',
'4', '[ 4 ]',
'5', '[ 5 ]',
'6', '[ 6 ]',
'7', '[ 7 ]',
'8', '[ 8 ]',
col_char)
USING current_cell_value, y_cord + i;
-- updates which cells have been visited
-- this prevents the recursion from being endless
SELECT count(*)
FROM visited v
WHERE v.positionX = x_cord + j AND v.positionY = y_cord + i
INTO is_visited;
-- if this cell hasn't been visited yet
IF is_visited = 0 THEN
-- insert it into the visited table
EXECUTE format('
INSERT INTO visited(positionX, positionY)
VALUES ($1, $2)
')
USING x_cord + j, y_cord + i;
-- only recursive function IF the user display is [ - ] AKA unrevealed
IF current_cell_value = '0' THEN
PERFORM zero_open(x_cord + j, y_cord + i);
END IF;
END IF;
END IF;
END IF;
END LOOP;
END LOOP;
-- get the number of cells have been visited
SELECT count(*)
FROM visited v
INTO is_visited;
-- update the number of revealed cells with the visited cells
UPDATE revealed r
SET revealedTiles = revealedTiles + is_visited
WHERE r.id = 1;
END;
$$ LANGUAGE plpgsql;
-- ### FUNCTION:
-- col_char = stored the character converted x-cord
-- x_cord = gets the current x-cord of the user
-- y_cord = gets the current y-cord of the user
-- uAction = gets the current action from the user
-- user_display_value =
CREATE OR REPLACE FUNCTION display_state() RETURNS TABLE(
"A" VARCHAR(40),
"B" VARCHAR(40),
"C" VARCHAR(40),
"D" VARCHAR(40),
"E" VARCHAR(40),
"F" VARCHAR(40),
"G" VARCHAR(40),
"H" VARCHAR(40),
"I" VARCHAR(40),
"J" VARCHAR(40),
"K" VARCHAR(40),
"L" VARCHAR(40),
"M" VARCHAR(40),
"N" VARCHAR(40),
"O" VARCHAR(40),
"P" VARCHAR(40)
)
LANGUAGE plpgsql AS $$
#variable_conflict use_column
DECLARE
x_cord INTEGER := 0;
y_cord INTEGER := 0;
col_char VARCHAR(1) := '';
uAction VARCHAR(10) := '';
user_display_value VARCHAR(10) := '';
BEGIN
-- get the user position
SELECT up.positionY, up.positionX
INTO y_cord, x_cord
FROM user_position up
WHERE up.id = 1;
-- get the user action
SELECT uA.action_type
INTO uAction
FROM user_action ua
WHERE ua.id = 1;
-- convert the x-cord into the a character
col_char := CHR(64 + x_cord);
-- find the current value of the user display cell
EXECUTE format('
SELECT %I
FROM user_display ud
WHERE row_id = $1
', col_char)
INTO user_display_value
USING y_cord;
-- store the value of the user display for the current cell
-- this'll be used for state management
UPDATE prev_tile pt
SET prev = user_display_value
WHERE pt.row_id = 1;
-- perform the user action
PERFORM enter_action(x_cord, y_cord, uAction);
-- get the user action from the table
SELECT uA.action_type
INTO uAction
FROM user_action ua
WHERE ua.id = 1;
-- if the action is L, then the game is over
IF uAction = 'L' THEN
RAISE NOTICE 'game over';
RETURN QUERY SELECT
'G'::VARCHAR(40) AS "A",
'A'::VARCHAR(40) AS "B",
'M'::VARCHAR(40) AS "C",
'E'::VARCHAR(40) AS "D",
'-'::VARCHAR(40) AS "E",
'O'::VARCHAR(40) AS "F",
'V'::VARCHAR(40) AS "G",
'E'::VARCHAR(40) AS "H",
'R'::VARCHAR(40) AS "I",
'-'::VARCHAR(40) AS "J",
'-'::VARCHAR(40) AS "K",
'-'::VARCHAR(40) AS "L",
'-'::VARCHAR(40) AS "M",
'-'::VARCHAR(40) AS "N",
'-'::VARCHAR(40) AS "O",
'-'::VARCHAR(40) AS "P";
ELSE
-- otherwise return the user_display
RETURN QUERY SELECT "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P"
FROM user_display
ORDER BY row_id;
END IF;
END;
$$;
-- ### FUNCTION:
-- x_cord = stores the user x-cord
-- y_cord = stores the user y-cord
-- col_char = stores the character converted x-cord
-- update_value =
-- current_cell_value = gets the current value from the user_display
-- uAction = get the current user action
-- in_flag = checks if the current cell is flagged
CREATE OR REPLACE FUNCTION clear_movement() RETURNS VOID AS $$
DECLARE
x_cord INTEGER := 0;
y_cord INTEGER := 0;
col_char VARCHAR(1) := '';
display_cell_value VARCHAR(40) := '[ - ]';
current_cell_value VARCHAR(1) := '';
uAction VARCHAR(1) := 'N';
in_flag INTEGER := 0;
BEGIN
-- get the user position
SELECT up.positionY, up.positionX
INTO y_cord, x_cord
FROM user_position up
WHERE up.id = 1;
-- convert the x-cord into a character
col_char := CHR(64 + x_cord);
-- get the current user action
SELECT uA.action_type
INTO uAction
FROM user_action ua
WHERE ua.id = 1;
-- get the value of the user display for the current cell
SELECT pt.prev
INTO display_cell_value
FROM prev_tile pt
WHERE pt.row_id = 1;
-- update the user display with the value of the user_display for the cell
-- this is needed because otherwise X would leave the cell blank
EXECUTE format('
UPDATE user_display ud
SET %I = $1
WHERE ud.row_id = $2
', col_char) USING display_cell_value, y_cord;
-- if the last action used was R
IF uAction = 'R' THEN
-- then get the value of the current cell
EXECUTE format('
SELECT mf.%I
FROM minefield mf
WHERE mf.row_id = $1', col_char)
INTO current_cell_value
USING y_cord;
-- replace the display cell value with the current bomb count
display_cell_value := '[ ' || current_cell_value || ' ]';
-- update the user display with the current bomb count
EXECUTE format('
UPDATE user_display ud
SET %I = $1
WHERE ud.row_id = $2', col_char)
USING display_cell_value, y_cord;
-- update the revealed cells + 1
UPDATE revealed r
SET revealedTiles = revealedTiles + 1
WHERE r.id = 1;
END IF;
-- if the last action used was R
IF uAction = 'F' THEN
-- find whether the current cell is flagged / exists in flag table
SELECT count(*)
FROM flags f
WHERE f.positionX = x_cord AND f.positionY = y_cord
INTO in_flag;
-- if the flag does exist & the current cell is not revealed / unopened
IF in_flag = 1 AND display_cell_value = '[ - ]' THEN
-- set the current cell to be flagged (user toggles)
EXECUTE format('
UPDATE user_display ud
SET %I = $1
WHERE ud.row_id = $2
', col_char) USING '[ F ]', y_cord;
END IF;
-- if the flag doesn't exist & the current cell is flagged
IF in_flag = 0 AND display_cell_value = '[ F ]' THEN
-- set the current cell to be unrevealed (user untoggles)
EXECUTE format('
UPDATE user_display ud
SET %I = $1
WHERE ud.row_id = $2
', col_char) USING '[ - ]', y_cord;
END IF;
END IF;
-- reset the user action
UPDATE user_action ua
SET action_type = 'N'
WHERE ua.id = 1;
END $$
LANGUAGE plpgsql;