Created
March 2, 2018 14:30
-
-
Save afarber/88a832a1b90a8940764ad69b2b761914 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE OR REPLACE FUNCTION words_play_game( | |
in_uid integer, | |
in_gid integer, | |
in_tiles jsonb | |
) RETURNS table ( | |
out_uid integer, -- the player to be notified | |
out_fcm text, | |
out_apns text, | |
out_adm text, | |
out_body text | |
) AS | |
$func$ | |
DECLARE | |
_tile jsonb; | |
_letter char; | |
_value integer; | |
_col integer; | |
_row integer; | |
_pos integer; | |
_mid bigint; | |
_total integer; | |
_hand_len integer; | |
_pile_len integer; | |
_move_len integer; | |
_pile char[]; | |
_hand char[]; | |
_letters char[][]; | |
_values integer[][]; | |
_opponent integer; | |
_finished timestamptz; | |
_reason text; | |
_score1 integer; | |
_score2 integer; | |
BEGIN | |
IF EXISTS (SELECT 1 FROM words_users | |
WHERE uid = in_uid AND | |
banned_until > CURRENT_TIMESTAMP) THEN | |
RAISE EXCEPTION 'User % is banned', in_uid; | |
END IF; | |
-- fetch the 4 arrays (_hand, _pile, _letters, _values) for the current game | |
SELECT | |
hand1, | |
pile, | |
letters, | |
values | |
INTO | |
_hand, | |
_pile, | |
_letters, | |
_values | |
FROM words_games WHERE | |
gid = in_gid AND | |
player1 = in_uid AND | |
-- game is not over yet | |
finished IS NULL AND | |
-- and it is first player's turn | |
(played1 IS NULL OR played1 < played2); | |
IF NOT FOUND THEN | |
SELECT | |
hand2, | |
pile, | |
letters, | |
values | |
INTO | |
_hand, | |
_pile, | |
_letters, | |
_values | |
FROM words_games WHERE | |
gid = in_gid AND | |
player2 = in_uid AND | |
-- game is not over yet | |
finished IS NULL AND | |
-- and it is second player's turn | |
(played2 IS NULL OR played2 < played1); | |
END IF; | |
IF NOT FOUND THEN | |
RAISE EXCEPTION 'Game % not found for user %', in_gid, in_uid; | |
END IF; | |
PERFORM words_check_positions(in_uid, in_gid, in_tiles); | |
FOR _tile IN SELECT * FROM JSONB_ARRAY_ELEMENTS(in_tiles) | |
LOOP | |
_letter := _tile->>'letter'; | |
_value := (_tile->>'value')::int; | |
_col := (_tile->>'col')::int + 1; | |
_row := (_tile->>'row')::int + 1; | |
IF NOT words_valid_tile(_letter, _value) THEN | |
RAISE EXCEPTION 'Invalid tile = %', _tile; | |
END IF; | |
-- search for the played tile in the player hand | |
IF _value = 0 THEN | |
_pos := ARRAY_POSITION(_hand, '*'); | |
ELSE | |
_pos := ARRAY_POSITION(_hand, _letter); | |
END IF; | |
IF _pos >= 1 THEN | |
_hand[_pos] := NULL; | |
ELSE | |
RAISE EXCEPTION 'Tile % not found in hand %', _tile, _hand; | |
END IF; | |
_letters[_col][_row] := _letter; | |
_values[_col][_row] := _value; | |
END LOOP; | |
-- remove played tiles from player hand | |
_hand := ARRAY_REMOVE(_hand, NULL); | |
-- move up to 7 missing tiles from pile to hand | |
_hand_len := CARDINALITY(_hand); | |
_pile_len := CARDINALITY(_pile); | |
_move_len := LEAST(7 - _hand_len, _pile_len); | |
_hand := _hand || _pile[1:_move_len]; | |
_pile := _pile[(1 + _move_len):_pile_len]; | |
INSERT INTO words_moves ( | |
action, | |
gid, | |
uid, | |
played, | |
tiles | |
) VALUES ( | |
'play', | |
in_gid, | |
in_uid, | |
CURRENT_TIMESTAMP, | |
in_tiles | |
) RETURNING mid INTO STRICT _mid; | |
INSERT INTO words_scores ( | |
mid, | |
gid, | |
uid, | |
word, | |
score | |
) ( SELECT | |
_mid, | |
in_gid, | |
in_uid, | |
out_word, | |
max(out_score) | |
FROM words_check_words(in_uid, in_gid, in_tiles) | |
GROUP BY out_word); | |
SELECT | |
SUM(score), | |
words_get_given(in_uid) || ': ' || STRING_AGG(FORMAT('%s (%s)', word, score), ', ') | |
INTO STRICT | |
_total, | |
out_body | |
FROM words_scores | |
WHERE mid = _mid; | |
if _move_len = 7 THEN | |
_total := _total + 15; | |
out_body := out_body || ' +15 бонус'; | |
END IF; | |
-- player has no tiles, game over | |
IF CARDINALITY(_hand) = 0 THEN | |
_finished := CURRENT_TIMESTAMP; | |
_reason := 'regular'; | |
-- TODO append win, loss, draw to out_body | |
END IF; | |
UPDATE words_moves SET | |
score = _total | |
WHERE mid = _mid; | |
-- RAISE NOTICE '_hand = %', _hand; | |
-- RAISE NOTICE '_pile = %', _pile; | |
-- RAISE NOTICE '_letters = %', _letters; | |
-- RAISE NOTICE '_values = %', _values; | |
-- RAISE NOTICE '_hand_len = %', _hand_len; | |
-- RAISE NOTICE '_pile_len = %', _pile_len; | |
-- RAISE NOTICE '_move_len = %', _move_len; | |
-- RAISE NOTICE '_total = %', _total; | |
-- TODO update score and store played words and stats | |
UPDATE words_games SET | |
finished = _finished, | |
reason = _reason, | |
played1 = CURRENT_TIMESTAMP, | |
score1 = score1 + _total, | |
hand1 = _hand, | |
pile = _pile, | |
letters = _letters, | |
values = _values, | |
state1 = words_get_state(_finished, score1 + _total, score2), | |
state2 = words_get_state(_finished, score2, score1 + _total), | |
hint1 = words_get_hint(_finished, FALSE, score1 + _total, score2), | |
hint2 = words_get_hint(_finished, TRUE, score2, score1 + _total) | |
WHERE | |
gid = in_gid AND | |
player1 = in_uid AND | |
-- game is not over yet | |
finished IS NULL AND | |
-- and it is first player's turn | |
(played1 IS NULL OR played1 < played2) | |
RETURNING | |
player2, | |
score1, | |
score2 | |
INTO | |
_opponent, | |
_score1, | |
_score2; | |
IF NOT FOUND THEN | |
UPDATE words_games SET | |
finished = _finished, | |
reason = _reason, | |
played2 = CURRENT_TIMESTAMP, | |
score2 = score2 + _total, | |
hand2 = _hand, | |
pile = _pile, | |
letters = _letters, | |
values = _values, | |
state1 = words_get_state(_finished, score1, score2 + _total), | |
state2 = words_get_state(_finished, score2 + _total, score1), | |
hint1 = words_get_hint(_finished, TRUE, score1, score2 + _total), | |
hint2 = words_get_hint(_finished, FALSE, score2 + _total, score1) | |
WHERE | |
gid = in_gid AND | |
player2 = in_uid AND | |
-- game is not over yet | |
finished IS NULL AND | |
-- and it is second player's turn | |
(played2 IS NULL OR played2 < played1) | |
RETURNING | |
player1, | |
score2, | |
score1 | |
INTO | |
_opponent, | |
_score1, | |
_score2; | |
END IF; | |
IF NOT FOUND THEN | |
RAISE EXCEPTION 'Game % not found for user %', in_gid, in_uid; | |
END IF; | |
-- this is the very first move in 1-player game, notification not needed | |
IF _opponent IS NULL THEN | |
RETURN; | |
END IF; | |
SELECT | |
_opponent, | |
fcm, | |
apns, | |
adm | |
FROM words_users | |
WHERE uid = _opponent | |
INTO STRICT | |
out_uid, | |
out_fcm, | |
out_apns, | |
out_adm; | |
-- add 1 row (containing notification) to the output table | |
RETURN NEXT; | |
END | |
$func$ LANGUAGE plpgsql; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment