/*\ |*| parabola web match game |*| copyright (C) 2019 bill-auger |*| |*| This program is free software: you can redistribute it and/or modify |*| it under the terms of the GNU Affero General Public License as published by |*| the Free Software Foundation, either version 3 of the License, or |*| (at your option) any later version. |*| |*| This program is distributed in the hope that it will be useful, |*| but WITHOUT ANY WARRANTY; without even the implied warranty of |*| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |*| GNU Affero General Public License for more details. |*| |*| You should have received a copy of the GNU Affero General Public License |*| along with this program. If not, see . \*/ console.log("[match_game]: IN" + ((DEBUG) ? " (DEBUG)" : '')) ; $('#card-table').css('width' , TABLE_W + 'px') ; $('#card-table').css('height' , TABLE_H + 'px') ; $(``).appendTo($('#card-table')) ; var MatchSound = new Audio(MATCH_SOUND_URL ) ; var MismatchSound = new Audio(MISMATCH_SOUND_URL) ; var CardSound = new Audio(CARD_SOUND_URL ) ; var DealSound = new Audio(DEAL_SOUND_URL ) ; var DoneSound = new Audio(DONE_SOUND_URL ) ; var IsLocked = false ; var Rows = [] ; var GameState = [] ; var UnusedDeck ; var DealDeck ; var DisplayDeck1 ; var DisplayDeck2 ; /* business */ $(document).ready(()=> { $('#start-button').click(()=> { Init() ; StartGame() ; }) ; if (DEBUG) $('#start-button').click() ; }) ; function Init() { if (DEBUG) console.log("Init()") ; cards.init({ table: '#card-table' }) ; var used_cards = cards.all.filter(card => card.rank >= 1 && card.rank <= 12 && card.suit == 'c') ; var unused_cards = cards.all.filter(card => !(~used_cards.indexOf(card))) ; UnusedDeck = new cards.Deck({ faceUp: false }) ; DealDeck = new cards.Deck({ faceUp: false }) ; DisplayDeck1 = new cards.Deck({ faceUp: false }) ; DisplayDeck2 = new cards.Deck({ faceUp: false }) ; UnusedDeck.addCards(unused_cards) ; DealDeck .addCards(used_cards ) ; for (var row_n = 0 ; row_n < N_ROWS ; ++row_n) { var row_y = (CARD_H / 2) + ROW_PAD + (ROW_H * row_n) ; var a_row = new cards.Hand({ faceUp: false , y: row_y }) ; Rows.push({ row: a_row , cache: [] }) ; } UnusedDeck .x += (TABLE_W / 2) + CARD_W ; DealDeck .x += (TABLE_W / 2) + CARD_W - 5 ; DealDeck .y -= 5 ; DisplayDeck1.x += (TABLE_W / 2) + CARD_W ; DisplayDeck2.x += (TABLE_W / 2) + CARD_W ; DisplayDeck1.y -= CARD_H + ROW_PAD ; DisplayDeck2.y += CARD_H + ROW_PAD ; UnusedDeck.render({ immediate: true }) ; DealDeck .render({ immediate: true }) ; } function StartGame() { if (DEBUG) console.log("StartGame()") ; var best_time_msg = (HAS_BEST_TIME ) ? `
${BEST_TIME[ 'login']}: ${BEST_TIME ['elapsed']} seconds` : '' ; var best_tries_msg = (HAS_BEST_TRIES) ? `
${BEST_TRIES['login']}: ${BEST_TRIES['n_tries']} guesses` : '' ; var bests_msg = "Best attempts today:" + best_time_msg + best_tries_msg ; if (HAS_BEST_ATTEMPTS) $('#start-div').html(bests_msg) ; setTimeout(()=> { $('#start-div').hide() ; DealSound.play() ; DealDeck.deal(N_COLS , Rows.map(row => row['row']) , ANIM_SPEED , ()=> { for (var row_n = 0 ; row_n < N_ROWS ; ++row_n) { var a_row = Rows[row_n] ; a_row['row'].forEach((a_card)=> { if (DEBUG) DbgDecorateCard(a_card) ; a_card['row-n'] = row_n ; a_row['cache'].push(a_card) ; }) ; a_row['row'].click(HandleCardClicked) ; } DisplayStats(ParseResp(',0,0,0,0')) ; }) ; } , STARTDIV_DELAY) ; } function HandleCardClicked(a_card) { a_card.el.addClass('active-card') ; if (!IsLocked) SetLockState(true) ; else return ; CardSound.play() ; a_card.showCard() ; setTimeout(()=> { var a_deck ; var should_verify ; if (!DisplayDeck1.faceUp) { a_deck = DisplayDeck1 ; should_verify = false ; } else if (!DisplayDeck2.faceUp) { a_deck = DisplayDeck2 ; should_verify = true ; } else return ; if (DEBUG) console.log("HandleCardClicked() a_card.name=" + a_card.name) ; a_deck.faceUp = true ; a_deck.addCard(a_card) ; a_deck.render() ; if (should_verify) SubmitGameState() ; else SetLockState(false) ; } , CARD_DELAY) ; } function SubmitGameState() { var card_1 = DisplayDeck1.topCard() ; var card_2 = DisplayDeck2.topCard() ; var game_state = card_1.suit + card_1.rank + '-' + card_2.suit + card_2.rank ; if (DEBUG) console.log("SubmitGameState() game_state=" + game_state) ; $.ajax( { url : '/account/register.js' , type : 'POST' , data : { pairs : GameState.concat([ game_state ]).join(',') , authenticity_token : $('meta[name=csrf-token]').attr('content') } }).always((resp)=> { HandleResult(resp.responseText , game_state) ; } ) ; } function HandleResult(resp_csv , game_state) { var stats = ParseResp(resp_csv) ; var is_match = stats['status'] == 'ok' ; var is_best = stats['is_best_time'] || stats['is_best_tries'] ; var stats_msg = `You complete the puzzle in ${stats['elapsed']} seconds
` + `with ${stats['n_tries']} guesses` + ((is_best ) ? '

' : '') + ((stats['is_best_time' ]) ? "Yay! That was the fastest time today!
" : '') + ((stats['is_best_tries']) ? "Yay! That was the fewest guesses today!" : '') ; if (DEBUG) console.log("HandleResult() stats=" + JSON.stringify(stats)) ; if (DEBUG) console.log("HandleResult() is_match=" + is_match) ; if (is_match) { MatchSound .play() ; GameState.push(game_state) ; } else { MismatchSound.play() ; } DisplayStats(stats) ; setTimeout(()=> { NextState(is_match , stats_msg) ; } , NEXTSTATE_DELAY) ; } function NextState(is_match , stats_msg) { DisplayDeck1.faceUp = false ; DisplayDeck1.render() ; DisplayDeck2.faceUp = false ; DisplayDeck2.render() ; var card_1 = DisplayDeck1.topCard() ; var rowdata_1 = Rows[card_1['row-n']] ; var card_2 = DisplayDeck2.topCard() ; var rowdata_2 = Rows[card_2['row-n']] ; var cache_1 = rowdata_1['cache'] ; var row_1 = rowdata_1['row'] ; var cache_2 = rowdata_2['cache'] ; var row_2 = rowdata_2['row'] ; if (is_match) { cache_1.splice(cache_1.indexOf(card_1) , 1) ; cache_2.splice(cache_2.indexOf(card_2) , 1) ; } else { while (row_1.length > 0) row_1.removeCard(row_1[row_1.length - 1]) ; while (row_2.length > 0) row_2.removeCard(row_2[row_2.length - 1]) ; cache_1.forEach((a_card)=> { row_1.addCard(a_card) ; }) ; cache_2.forEach((a_card)=> { row_2.addCard(a_card) ; }) ; } card_1.el.removeClass('active-card') ; row_1.render() ; card_2.el.removeClass('active-card') ; row_2.render() ; var is_completed = DisplayDeck1.length == 6 && DisplayDeck2.length == 6 ; if (is_completed) { DoneSound.play() ; $('#completed-div').show() ; $(`

${stats_msg}

`).appendTo($('#completed-div')) ; setTimeout(()=> { location.reload() ; } , RELOAD_DELAY) ; } else SetLockState(false) ; } /* helpers */ function ParseResp(resp_csv) { if (DEBUG) console.log("ParseResp() resp_csv=" + resp_csv) ; var resp = resp_csv.split(',') ; var stats = (resp.length != N_RESP_PARAMS) ? {} : { 'status' : ((resp[0].match(/^ok$/ )) ? resp[0] : '' ) , 'elapsed' : ((resp[1].match(/^\d+$/ )) ? resp[1] : 'ERR') , 'n_tries' : ((resp[2].match(/^\d+$/ )) ? resp[2] : 'ERR') , 'is_best_time' : ((resp[3].match(/^(true|false)$/ )) ? resp[3] : 'ERR') , 'is_best_tries' : ((resp[4].match(/^(true|false)$/ )) ? resp[4] : 'ERR') } ; return stats ; } function DisplayStats(stats) { $('#elapsed-span').text(stats['elapsed']) ; $('#elapsed-div').show() ; $('#tries-span' ).text(stats['n_tries']) ; $('#tries-div' ).show() ; } function SetLockState(is_locked) { if (DEBUG) console.log("SetLockState() " + IsLocked + "=>" + is_locked) ; IsLocked = is_locked ; $('.card').css('opacity' , (is_locked) ? LOCKED_OPACITY : 1.0) ; $('.active-card').css('opacity' , 1.0) ; }