source: pages/api/blackjack/index.js@ fe03f69

main
Last change on this file since fe03f69 was 87614a5, checked in by anastasovv <simon@…>, 2 years ago

Blackjack prototype

  • Property mode set to 100644
File size: 16.5 KB
Line 
1import { v4 as uuidv4 } from 'uuid';
2
3import axios from 'axios';
4
5require('dotenv').config();
6
7/**
8 * ********************* BEGIN OF DEALING WITH GAME STATES *********************
9 */
10
11const singleDeck = ["SA", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "SX", "SJ", "SQ", "SK",
12 "HA", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "HX", "HJ", "HQ", "HK",
13 "CA", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CX", "CJ", "CQ", "CK",
14 "DA", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DX", "DJ", "DQ", "DK" ];
15
16/* We are using 5 decks */
17const deck = singleDeck.concat(singleDeck).concat(singleDeck).concat(singleDeck).concat(singleDeck);
18
19let game = {
20 deck: [...deck],
21 status: '_1_room_created',
22 playerCards: [],
23 dealerName: 'Lazar',
24 dealerCards: [],
25 initialBet: 0,
26 sideBet: 0,
27 sideBetName: '',
28 outcome: '',
29 earnings: 0, // positive for draw, 2x for win, negative for loss.
30 sideBetOutcome: '',
31 sideBetEarnings: 0,
32}
33
34let rooms = []
35
36/**
37 * Replace deck if empty
38 */
39function checkDeckSize(game) {
40 if (game.deck.length === 0) {
41 game.deck = [...deck];
42 }
43}
44
45/**
46 * Draw a SINGLE random card
47 */
48function drawASingleCard(room) {
49 checkDeckSize(room);
50 let idx = Math.floor(Math.random() * room.deck.length);
51 let card = room.deck[idx];
52
53 room.deck.splice(idx, 1);
54
55 return card;
56}
57
58/**
59 * Deal the initial hand of cards
60 */
61function getInitialCards(room) {
62 room.playerCards.push(drawASingleCard(room));
63 room.playerCards.push(drawASingleCard(room));
64
65 room.dealerCards.push(drawASingleCard(room));
66 room.dealerCards.push(drawASingleCard(room));
67}
68
69function calculateEarnings(room) {
70 let betEarnings = 0;
71
72 if (room.outcome === 'draw') {
73 betEarnings = room.initialBet;
74 }
75 else if (room.outcome === 'player_won' || room.outcome === 'dealer_busted') {
76 betEarnings = 2 * room.initialBet;
77 }
78 else if (room.outcome === 'player_lost' || room.outcome === 'player_busted') {
79 betEarnings = -1 * room.initialBet;
80 }
81
82 return betEarnings;
83}
84
85function calculateSideBetEarnings(room) {
86 let sideBetEarnings = -1 * room.sideBet;
87
88 if (room.sideBetName != '') {
89 if (room.sideBetName === 'mixed_pair') {
90 if (checkIfSameValue(room.playerCards)) {
91 sideBetEarnings = room.sideBet * 5;
92 }
93 }
94 else if (room.sideBetName === 'coloured_pair') {
95 if (checkIfSameValue(room.playerCards) && checkIfSameColour(room.playerCards)) {
96 sideBetEarnings = room.sideBet * 12;
97 }
98 }
99 else if (room.sideBetName === 'perfect_pair') {
100 if (checkIfSameValue(room.playerCards) && checkIfSameSuit(room.playerCards)) {
101 sideBetEarnings = room.sideBet * 25;
102 }
103 }
104 else if (room.sideBetName === 'flush') {
105 const tmpCards = room.playerCards.slice().concat(room.dealerCards[0]);
106 if (checkIfSameSuit(tmpCards)) {
107 sideBetEarnings = room.sideBet * 5;
108 }
109 }
110 else if (room.sideBetName === 'straight') {
111 const tmpCards = room.playerCards.slice().concat(room.dealerCards[0]);
112 if (checkIfStraight(tmpCards)) {
113 sideBetEarnings = room.sideBet * 10;
114 }
115 }
116 else if (room.sideBetName === 'three_of_a_kind') {
117 const tmpCards = room.playerCards.slice().concat(room.dealerCards[0]);
118 if (checkIfSameValue(tmpCards)) {
119 sideBetEarnings = room.sideBet * 30;
120 }
121 }
122 else if (room.sideBetName === 'straight_flush') {
123 const tmpCards = room.playerCards.slice().concat(room.dealerCards[0]);
124 if (checkIfStraight(tmpCards) && checkIfSameSuit(tmpCards)) {
125 sideBetEarnings = room.sideBet * 40;
126 }
127 }
128 else if (room.sideBetName === 'suited_triple') {
129 const tmpCards = room.playerCards.slice().concat(room.dealerCards[0]);
130 if (checkIfSameSuit(tmpCards) && checkIfSameValue(tmpCards)) {
131 sideBetEarnings = room.sideBet * 100;
132 }
133 }
134 }
135
136 return sideBetEarnings;
137}
138
139/**
140 * Set up a room
141 */
142function createARoom(session_id) {
143 let room = {
144 ...game, playerCards: [...game.playerCards], dealerCards: [...game.dealerCards],
145 }
146
147 rooms[session_id] = room;
148}
149/**
150 * ********************* END OF DEALING WITH GAME STATES *********************
151 */
152
153/**
154 * ********************* BEGIN OF REQUEST HANDLER *********************
155 */
156export default async function handler(req, res) {
157 /**
158 * GET method
159 */
160 if (req.method === 'GET') {
161 /**
162 * /---------------------- GET ----------------------/
163 * @action play_again
164 * @param session_id
165 */
166 if (req.query.action === 'play_again' && req.query?.session_id) {
167 const session_id = req.query.session_id;
168
169 if (rooms[session_id] !== undefined && rooms[session_id].status.substr(1, 1) === '5') {
170 rooms[session_id] = {...game, playerCards: [...game.playerCards], dealerCards: [...game.dealerCards]};
171
172 res.json({
173 success: true,
174 game: rooms[session_id],
175 })
176
177 return ;
178 }
179
180 res.json({
181 success: false,
182 })
183 }
184
185 /**
186 * /---------------------- GET ----------------------/
187 * @action stand
188 * @param session_id
189 */
190 if (req.query.action === 'stand' && req.query?.session_id) {
191 const session_id = req.query.session_id;
192
193 if (rooms[session_id] !== undefined && rooms[session_id].status.substr(1, 1) === '4') {
194 const room = rooms[session_id];
195
196 while (calculateHandValue(room.dealerCards) < 17) {
197 room.dealerCards.push(drawASingleCard(room));
198 }
199
200 room.status = '_5_game_over';
201
202 if (calculateHandValue(room.dealerCards) > 21) {
203 room.outcome = 'dealer_busted';
204 }
205 else if (calculateHandValue(room.playerCards) > calculateHandValue(room.dealerCards)) {
206 room.outcome = 'player_won';
207 }
208 else if (calculateHandValue(room.playerCards) < calculateHandValue(room.dealerCards)) {
209 room.outcome = 'player_lost';
210 }
211 else {
212 room.outcome = 'draw';
213 }
214
215 room.earnings = calculateEarnings(room);
216
217 rooms[session_id] = room;
218
219 axios.get(`${process.env.HOME_URL}/api/postgre/?action=add_credits&session_id=${session_id}&credits=${room.earnings}&game=blackjack&outcome=${room.outcome}`).then(postgreRes => {
220 if (postgreRes.data?.success) {
221 res.json({
222 success: true,
223 status: rooms[session_id].status,
224 playerCards: rooms[session_id].playerCards,
225 dealerCards: rooms[session_id].dealerCards,
226 outcome: rooms[session_id].outcome,
227 earnings: rooms[session_id].earnings,
228 credits: postgreRes.data?.credits,
229 })
230 }
231 else {
232 res.json({
233 success: false,
234 })
235 }
236 });
237
238 return ;
239 }
240
241 res.json({
242 success: false,
243 })
244 }
245
246 /**
247 * /---------------------- GET ----------------------/
248 * @action hit_a_card
249 * @param session_id
250 */
251 if (req.query.action === 'hit_a_card' && req.query?.session_id) {
252 const session_id = req.query.session_id;
253
254 if (rooms[session_id] !== undefined && rooms[session_id].status.substr(1, 1) === '4') {
255 const room = rooms[session_id];
256
257 room.playerCards.push(drawASingleCard(room));
258
259 rooms[session_id] = room;
260
261 if (calculateHandValue(room.playerCards) > 21) {
262 room.status = '_5_game_over';
263 room.outcome = 'player_busted';
264
265 room.earnings = calculateEarnings(room);
266
267 rooms[session_id] = room;
268
269 axios.get(`${process.env.HOME_URL}/api/postgre/?action=add_credits&session_id=${session_id}&credits=${room.earnings}&game=blackjack&outcome=${room.outcome}`).then(postgreRes => {
270 if (postgreRes.data?.success) {
271 res.json({
272 success: true,
273 status: rooms[session_id].status,
274 playerCards: rooms[session_id].playerCards,
275 outcome: rooms[session_id].outcome,
276 earnings: rooms[session_id].earnings,
277 credits: postgreRes.data?.credits,
278 })
279 }
280 else {
281 res.json({
282 success: false,
283 })
284 }
285 });
286 }
287 else {
288 res.json({
289 success: true,
290 status: rooms[session_id].status,
291 playerCards: rooms[session_id].playerCards,
292 outcome: rooms[session_id].outcome,
293 earnings: rooms[session_id].earnings,
294 })
295 }
296
297 return ;
298 }
299
300 res.json({
301 success: false,
302 })
303 }
304
305 /**
306 * /---------------------- GET ----------------------/
307 * @action get_initial_cards
308 * @param session_id
309 */
310 if (req.query.action === 'get_initial_cards' && req.query?.session_id) {
311 const session_id = req.query.session_id;
312
313 if (rooms[session_id] !== undefined && rooms[session_id].status.substr(1, 1) === '3') {
314 const room = rooms[session_id];
315
316 getInitialCards(room);
317
318 room.status = '_4_cards_on_the_table';
319
320 rooms[session_id] = room;
321
322 if (room.sideBetName !== '' && room.sideBetName !== 'none') {
323 room.sideBetEarnings = calculateSideBetEarnings(room);
324 room.sideBetOutcome = room.sideBetEarnings > 0 ? 'side_bet_won' : 'side_bet_lost';
325
326 rooms[session_id] = room;
327
328 axios.get(`${process.env.HOME_URL}/api/postgre/?action=add_credits&session_id=${session_id}&credits=${room.sideBetEarnings}`).then(postgreRes => {
329 if (postgreRes.data?.success) {
330 res.json({
331 success: true,
332 status: rooms[session_id].status,
333 playerCards: rooms[session_id].playerCards,
334 dealerCards: Array(rooms[session_id].dealerCards[0]).concat('back'),
335 sideBetOutcome: rooms[session_id].sideBetOutcome,
336 sideBetEarnings: rooms[session_id].sideBetEarnings,
337 credits: postgreRes.data?.credits,
338 })
339 }
340 else {
341 res.json({
342 success: false,
343 })
344 }
345 });
346 }
347 else {
348 res.json({
349 success: true,
350 status: rooms[session_id].status,
351 playerCards: rooms[session_id].playerCards,
352 dealerCards: Array(rooms[session_id].dealerCards[0]).concat('back'),
353 sideBetOutcome: rooms[session_id].sideBetOutcome,
354 sideBetEarnings: rooms[session_id].sideBetEarnings,
355 })
356 }
357
358 return ;
359 }
360
361 res.json({
362 success: false,
363 })
364 }
365
366 /**
367 * /---------------------- GET ----------------------/
368 * @action make_side_bet
369 * @param session_id
370 * @param bet
371 * @param betName
372 */
373 if (req.query.action === 'make_side_bet' && req.query?.session_id && req.query?.bet && req.query?.betName) {
374 const session_id = req.query.session_id;
375
376 if (rooms[session_id] !== undefined && rooms[session_id].status.substr(1, 1) === '2') {
377 if (req.query?.skip !== 'true' && parseInt(req.query.bet) <= 0) {
378 return ;
379 }
380
381 const room = rooms[session_id];
382
383 room.sideBet = parseInt(req.query.bet);
384 room.sideBetName = req.query.betName;
385 room.status = '_3_made_side_bet';
386
387 rooms[session_id] = room;
388
389 res.json({
390 success: true,
391 status: rooms[session_id].status,
392 })
393
394 return ;
395 }
396
397 res.json({
398 success: false,
399 })
400 }
401
402 /**
403 * /---------------------- GET ----------------------/
404 * @action make_initial_bet
405 * @param session_id
406 * @param bet
407 */
408 if (req.query.action === 'make_initial_bet' && req.query?.session_id && req.query?.bet) {
409 const session_id = req.query.session_id;
410
411 if (rooms[session_id] !== undefined && rooms[session_id].status.substr(1, 1) === '1') {
412 if (parseInt(req.query.bet) <= 0) return ;
413
414 const room = rooms[session_id];
415
416 room.initialBet = parseInt(req.query.bet);
417 room.status = '_2_made_initial_bet';
418
419 rooms[session_id] = room;
420
421 res.json({
422 success: true,
423 status: rooms[session_id].status,
424 })
425
426 return ;
427 }
428
429 res.json({
430 success: false,
431 })
432 }
433
434 /**
435 * /---------------------- GET ----------------------/
436 * @action remove_room
437 * @param session_id
438 */
439 if (req.query.action === 'remove_room' && req.query?.session_id) {
440 const session_id = req.query.session_id;
441
442 if (rooms[session_id] !== undefined) {
443 delete rooms[session_id];
444 }
445
446 res.json({
447 success: true,
448 })
449 }
450
451 /**
452 * /---------------------- GET ----------------------/
453 * @action get_player_info_on_enter
454 * @param session_id
455 */
456 if (req.query.action === 'get_player_info_on_enter' && req.query?.session_id) {
457 const session_id = req.query.session_id;
458
459 if (rooms[session_id] !== undefined) {
460
461 }
462 else {
463 createARoom(session_id);
464 }
465
466 let dealerCardsTmp = [];
467 if (rooms[session_id].status.substr(1, 1) != '1') { // 5 == game_over
468 rooms[session_id].dealerCards.forEach((card, i) => {
469 if (i === 0) {
470 dealerCardsTmp.push(card);
471 }
472 else {
473 dealerCardsTmp.push('back');
474 }
475 })
476 }
477
478 res.json({
479 success: true,
480 status: rooms[session_id].status,
481 initialBet: rooms[session_id].initialBet,
482 sideBet: rooms[session_id].sideBet,
483 sideBetName: rooms[session_id].sideBetName,
484 playerCards: rooms[session_id].playerCards,
485 dealerCards: dealerCardsTmp,
486 outcome: rooms[session_id].outcome,
487 earnings: rooms[session_id].earnings,
488 })
489 }
490 }
491}
492/**
493 * ********************* END OF REQUEST HANDLER *********************
494 */
495
496/**
497 * ********************* BEGIN OF FUNCTIONS THAT CHECK CARD COMBINATIONS *********************
498 */
499
500function calculateHandValue(cards) {
501 let value = 0;
502 let aces = 0;
503 for (let i = 0; i < cards.length; i++) {
504 let card = cards[i];
505 if (card.substring(1) === 'A') {
506 value += 11;
507 aces++;
508 } else if (card.substring(1) === 'X' || card.substring(1) === 'J' || card.substring(1) === 'Q' || card.substring(1) === 'K') {
509 value += 10;
510 } else {
511 value += parseInt(card.substring(1));
512 }
513 }
514 while (value > 21 && aces > 0) {
515 value -= 10;
516 aces--;
517 }
518 return value;
519}
520
521function checkIfSameValue(cards) {
522 for (let i = 1; i < cards.length; i++) {
523 if (cards[i][1] !== cards[i-1][1]) {
524 return false;
525 }
526 }
527
528 return true;
529}
530
531function checkIf2CardsAreSameColour(card1, card2) {
532 if (card1[0] === card2[0]) return true;
533 if (card1[0] === 'H' && card2[0] === 'D') return true;
534 if (card1[0] === 'D' && card2[0] === 'H') return true;
535 if (card1[0] === 'S' && card2[0] === 'C') return true;
536 if (card1[0] === 'C' && card2[0] === 'S') return true;
537 return false;
538}
539
540function checkIfSameColour(cards) {
541 for (let i = 1; i < cards.length; i++) {
542 if (!checkIf2CardsAreSameColour(cards[i], cards[i-1])) {
543 return false;
544 }
545 }
546
547 return true;
548}
549
550function checkIfSameSuit(cards) {
551 for (let i = 1; i < cards.length; i++) {
552 if (cards[i][0] !== cards[i-1][0]) {
553 return false;
554 }
555 }
556
557 return true;
558}
559
560function checkIfStraight(cards) {
561 let values = ['A', '2', '3', '4', '5', '6', '7', '8', '9', 'X', 'J', 'Q', 'K'];
562
563 let valuesInCards = [];
564 for (let i = 0; i < cards.length; i++) {
565 valuesInCards.push(cards[i][1]);
566 }
567
568 let temp = values.reduce((c, v, i) => Object.assign(c, {[v]: i}), {});
569
570 valuesInCards = valuesInCards.sort((a, b) => temp[a] - temp[b]);
571
572 let idx = values.indexOf(valuesInCards[0]);
573
574 let straight = true;
575
576 for (let i = 0; i < valuesInCards.length; i++) {
577 if (valuesInCards[i] !== values[idx]) {
578 straight = false;
579 break;
580 }
581
582 idx++;
583 if (idx >= temp.length) {
584 straight = false;
585 break;
586 }
587 }
588
589 if (straight) {
590 return true;
591 }
592
593 values = ['2', '3', '4', '5', '6', '7', '8', '9', 'X', 'J', 'Q', 'K', 'A'];
594 temp = values.reduce((c, v, i) => Object.assign(c, {[v]: i}), {});
595
596 valuesInCards = valuesInCards.sort((a, b) => temp[a] - temp[b]);
597
598 idx = values.indexOf(valuesInCards[0]);
599
600 for (let i = 0; i < valuesInCards.length; i++) {
601 if (valuesInCards[i] !== values[idx]) return false;
602
603 idx++;
604 if (idx >= temp.length) return false;
605 }
606
607 return true;
608}
609/**
610 * ********************* END OF FUNCTIONS THAT CHECK CARD COMBINATIONS *********************
611 */
Note: See TracBrowser for help on using the repository browser.