belote
Version:
A TypeScript library for playing Belote card game with bot support.
1 lines • 30.9 kB
JavaScript
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.BelotAPI=t():e.BelotAPI=t()}(this,()=>(()=>{"use strict";var e={784:e=>{var t,r="object"==typeof Reflect?Reflect:null,a=r&&"function"==typeof r.apply?r.apply:function(e,t,r){return Function.prototype.apply.call(e,t,r)};t=r&&"function"==typeof r.ownKeys?r.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var i=Number.isNaN||function(e){return e!=e};function n(){n.init.call(this)}e.exports=n,e.exports.once=function(e,t){return new Promise(function(r,a){function i(r){e.removeListener(t,n),a(r)}function n(){"function"==typeof e.removeListener&&e.removeListener("error",i),r([].slice.call(arguments))}f(e,t,n,{once:!0}),"error"!==t&&function(e,t){"function"==typeof e.on&&f(e,"error",t,{once:!0})}(e,i)})},n.EventEmitter=n,n.prototype._events=void 0,n.prototype._eventsCount=0,n.prototype._maxListeners=void 0;var s=10;function o(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function l(e){return void 0===e._maxListeners?n.defaultMaxListeners:e._maxListeners}function c(e,t,r,a){var i,n,s,c;if(o(r),void 0===(n=e._events)?(n=e._events=Object.create(null),e._eventsCount=0):(void 0!==n.newListener&&(e.emit("newListener",t,r.listener?r.listener:r),n=e._events),s=n[t]),void 0===s)s=n[t]=r,++e._eventsCount;else if("function"==typeof s?s=n[t]=a?[r,s]:[s,r]:a?s.unshift(r):s.push(r),(i=l(e))>0&&s.length>i&&!s.warned){s.warned=!0;var d=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");d.name="MaxListenersExceededWarning",d.emitter=e,d.type=t,d.count=s.length,c=d,console&&console.warn&&console.warn(c)}return e}function d(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function h(e,t,r){var a={fired:!1,wrapFn:void 0,target:e,type:t,listener:r},i=d.bind(a);return i.listener=r,a.wrapFn=i,i}function u(e,t,r){var a=e._events;if(void 0===a)return[];var i=a[t];return void 0===i?[]:"function"==typeof i?r?[i.listener||i]:[i]:r?function(e){for(var t=new Array(e.length),r=0;r<t.length;++r)t[r]=e[r].listener||e[r];return t}(i):g(i,i.length)}function m(e){var t=this._events;if(void 0!==t){var r=t[e];if("function"==typeof r)return 1;if(void 0!==r)return r.length}return 0}function g(e,t){for(var r=new Array(t),a=0;a<t;++a)r[a]=e[a];return r}function f(e,t,r,a){if("function"==typeof e.on)a.once?e.once(t,r):e.on(t,r);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,function i(n){a.once&&e.removeEventListener(t,i),r(n)})}}Object.defineProperty(n,"defaultMaxListeners",{enumerable:!0,get:function(){return s},set:function(e){if("number"!=typeof e||e<0||i(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");s=e}}),n.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},n.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||i(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},n.prototype.getMaxListeners=function(){return l(this)},n.prototype.emit=function(e){for(var t=[],r=1;r<arguments.length;r++)t.push(arguments[r]);var i="error"===e,n=this._events;if(void 0!==n)i=i&&void 0===n.error;else if(!i)return!1;if(i){var s;if(t.length>0&&(s=t[0]),s instanceof Error)throw s;var o=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw o.context=s,o}var l=n[e];if(void 0===l)return!1;if("function"==typeof l)a(l,this,t);else{var c=l.length,d=g(l,c);for(r=0;r<c;++r)a(d[r],this,t)}return!0},n.prototype.addListener=function(e,t){return c(this,e,t,!1)},n.prototype.on=n.prototype.addListener,n.prototype.prependListener=function(e,t){return c(this,e,t,!0)},n.prototype.once=function(e,t){return o(t),this.on(e,h(this,e,t)),this},n.prototype.prependOnceListener=function(e,t){return o(t),this.prependListener(e,h(this,e,t)),this},n.prototype.removeListener=function(e,t){var r,a,i,n,s;if(o(t),void 0===(a=this._events))return this;if(void 0===(r=a[e]))return this;if(r===t||r.listener===t)0===--this._eventsCount?this._events=Object.create(null):(delete a[e],a.removeListener&&this.emit("removeListener",e,r.listener||t));else if("function"!=typeof r){for(i=-1,n=r.length-1;n>=0;n--)if(r[n]===t||r[n].listener===t){s=r[n].listener,i=n;break}if(i<0)return this;0===i?r.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(r,i),1===r.length&&(a[e]=r[0]),void 0!==a.removeListener&&this.emit("removeListener",e,s||t)}return this},n.prototype.off=n.prototype.removeListener,n.prototype.removeAllListeners=function(e){var t,r,a;if(void 0===(r=this._events))return this;if(void 0===r.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==r[e]&&(0===--this._eventsCount?this._events=Object.create(null):delete r[e]),this;if(0===arguments.length){var i,n=Object.keys(r);for(a=0;a<n.length;++a)"removeListener"!==(i=n[a])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=r[e]))this.removeListener(e,t);else if(void 0!==t)for(a=t.length-1;a>=0;a--)this.removeListener(e,t[a]);return this},n.prototype.listeners=function(e){return u(this,e,!0)},n.prototype.rawListeners=function(e){return u(this,e,!1)},n.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):m.call(e,t)},n.prototype.listenerCount=m,n.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}}},t={};function r(a){var i=t[a];if(void 0!==i)return i.exports;var n=t[a]={exports:{}};return e[a](n,n.exports,r),n.exports}r.d=(e,t)=>{for(var a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};r.d(a,{default:()=>J});var i={};r.r(i),r.d(i,{Callings:()=>y,CardPoints:()=>g,CardPointsAdut:()=>f,GamePhase:()=>p,cardColors:()=>l,cardColorsLocal:()=>h,cardTypeOrder:()=>d,cardTypeOrderLocal:()=>m,cardTypes:()=>c,cardTypesLocal:()=>u});var n={};r.r(n),r.d(n,{canBeatCard:()=>j,canPlayCard:()=>B,cardColorLocalized:()=>R,cardTypeLocalized:()=>_,compareCallStrength:()=>x,createDeck:()=>k,findLegalCard:()=>O,firstLetterUpperCase:()=>P,getCardColorImageUrl:()=>b,getCardImageUrl:()=>T,getCardValue:()=>w,getHighestCall:()=>E,getHighestCardInSequence:()=>L,getHighestNonTrumpInTrick:()=>D,getHighestTrumpInTrick:()=>A,isConsecutiveSequence:()=>C,mulberry32:()=>S,normalizeLocalColor:()=>N,normalizeLocalType:()=>$,shuffleCards:()=>v,validateCalling:()=>I});var s={};r.r(s),r.d(s,{BotAI:()=>M});var o={};r.r(o),r.d(o,{Belote:()=>H});const l=["hearts","diamonds","clubs","spades"],c=["ace","king","queen","jack","ten","nine","eight","seven"],d=["seven","eight","nine","ten","jack","queen","king","ace"],h=["herc","karo","tref","pik"],u=["as","kralj","baba","decko","deset","devet","osam","sedam"],m=["sedam","osam","devet","deset","decko","baba","kralj","as"];var g,f,y,p;function S(e){return function(){let t=e+=1831565813;return t=Math.imul(t^t>>>15,1|t),t^=t+Math.imul(t^t>>>7,61|t),((t^t>>>14)>>>0)/4294967296}}function v(e,t=1,r){if(!Array.isArray(e))throw new Error("Deck must be an array of cards.");if(t<1)throw new Error("Times must be greater than 0.");const a=r??S(Date.now());for(let r=0;r<t;r++)for(let t=e.length-1;t>0;t--){const r=Math.floor(a()*(t+1));[e[t],e[r]]=[e[r],e[t]]}return e}function w(e,t){return e.color===t?f[P(e.type)]:g[P(e.type)]}function P(e){return 0===e.length?e:e.charAt(0).toUpperCase()+e.slice(1)}function T(e,t="medium",r){return`${r}/${t}/${R(e.color)}/${_(e.type)}.webp`}function b(e,t="medium",r){return`${r}/${t}/colors/${R(e)}.webp`}function k(e=1){const t=[];for(const e of l)for(const r of c)t.push({color:e,type:r});return v(t,e)}function C(e){for(let t=0;t<e.length-1;t++){const r=d.indexOf(e[t].type);if(d.indexOf(e[t+1].type)!==r+1)return!1}return!0}function I(e,t){let r=null;const a={hearts:[],diamonds:[],clubs:[],spades:[]},i={Ace:[],King:[],Queen:[],Jack:[],Ten:[],Nine:[],Eight:[],Seven:[]};for(const t of e)a[t.color].push(t),i[P(t.type)].push(t);const n=e=>{(!r||e.type>r.type)&&(r=e)};for(const t of l)if(8===e.length&&8===a[t].length)return{type:y.Belot};2===e.length&&a[t].some(e=>"king"===e.type)&&a[t].some(e=>"queen"===e.type)&&e.every(e=>e.color===t&&("king"===e.type||"queen"===e.type))&&n({type:y.Bela});for(const t of["Jack","Nine","Ace","King","Queen","Ten"])if(4===e.length&&4===i[t].length)switch(t){case"Jack":n({type:y.Jack4});break;case"Nine":n({type:y.Nine4});break;case"Ace":n({type:y.Ace4});break;case"King":n({type:y.King4});break;case"Queen":n({type:y.Queen4});break;case"Ten":n({type:y.Ten4})}if(e.length>=3){const t=e[0].color;if(e.every(e=>e.color===t)&&C(e.slice().sort((e,t)=>d.indexOf(e.type)-d.indexOf(t.type)))){const t=`Sequence${e.length}`;y[t]?n({type:y[t]}):e.length>=5&&n({type:y.Sequence5})}}return r}function E(e){return 0===e.length?null:e.reduce((e,t)=>e?x(t,e)>0?t:e:t)}function x(e,t){const r=e=>{switch(e){case y.Sequence3:return 1;case y.Sequence4:return 2;case y.Sequence5:return 3;case y.Sequence6:return 4;case y.Sequence7:return 5;case y.Bela:return 6;case y.Ace4:return 7;case y.King4:return 8;case y.Queen4:return 9;case y.Jack4:return 10;case y.Nine4:return 11;case y.Ten4:return 12;case y.Belot:return 13;default:return 0}},a=r(e.call),i=r(t.call);if(a!==i)return a-i;if(e.call>=y.Sequence3&&e.call<=y.Sequence7){const r=L(e.cards),a=L(t.cards);return g[P(r.type)]-g[P(a.type)]}return 0}function L(e){return e.reduce((e,t)=>g[P(t.type)]>g[P(e.type)]?t:e)}function B(e,t,r,a){if(0===t.length)return!0;const i=t[0].color;if(a.some(e=>e.color===i)){if(e.color!==i)return!1;if(i===r){const i=A(t,r);if(i&&j(e,i,r))return!a.some(e=>e.color===r&&j(e,i,r))||j(e,i,r)}else if(!t.some(e=>e.color===r)){const n=D(t,r);if(n&&j(e,n,r))return!a.some(e=>e.color===i&&j(e,n,r))||j(e,n,r)}return!0}if(a.some(e=>e.color===r)){if(e.color!==r)return!1;const i=A(t,r);if(i)return!a.some(e=>e.color===r&&j(e,i,r))||j(e,i,r)}return!0}function O(e,t,r){return r.find(a=>B(a,e,t,r))||null}function j(e,t,r){return e.color===r&&t.color===r?f[P(e.type)]>f[P(t.type)]:e.color===r&&t.color!==r||(e.color===r||t.color!==r)&&e.color===t.color&&g[P(e.type)]>g[P(t.type)]}function A(e,t){const r=e.filter(e=>e.color===t);return 0===r.length?null:r.reduce((e,t)=>f[P(t.type)]>f[P(e.type)]?t:e)}function D(e,t){const r=e.filter(e=>e.color!==t);return 0===r.length?null:r.reduce((e,t)=>g[P(t.type)]>g[P(e.type)]?t:e)}function R(e){switch(e){case"hearts":return"herc";case"diamonds":return"karo";case"clubs":return"tref";case"spades":return"pik"}}function _(e){switch(e){case"ace":return"as";case"king":return"kralj";case"queen":return"baba";case"jack":return"decko";case"ten":return"deset";case"nine":return"devet";case"eight":return"osam";case"seven":return"sedam"}}function N(e){switch(e){case"herc":return"hearts";case"karo":return"diamonds";case"tref":return"clubs";case"pik":return"spades"}}function $(e){switch(e){case"as":return"ace";case"kralj":return"king";case"baba":return"queen";case"decko":return"jack";case"deset":return"ten";case"devet":return"nine";case"osam":return"eight";case"sedam":return"seven"}}!function(e){e[e.Ace=11]="Ace",e[e.King=4]="King",e[e.Queen=3]="Queen",e[e.Jack=2]="Jack",e[e.Ten=10]="Ten",e[e.Nine=0]="Nine",e[e.Eight=0]="Eight",e[e.Seven=0]="Seven"}(g||(g={})),function(e){e[e.Ace=11]="Ace",e[e.King=4]="King",e[e.Queen=3]="Queen",e[e.Jack=20]="Jack",e[e.Ten=10]="Ten",e[e.Nine=14]="Nine",e[e.Eight=0]="Eight",e[e.Seven=0]="Seven"}(f||(f={})),function(e){e[e.None=0]="None",e[e.Belot=-1]="Belot",e[e.Bela=20]="Bela",e[e.Jack4=200]="Jack4",e[e.Nine4=150]="Nine4",e[e.Ace4=100]="Ace4",e[e.King4=100]="King4",e[e.Queen4=100]="Queen4",e[e.Ten4=100]="Ten4",e[e.Sequence3=20]="Sequence3",e[e.Sequence4=50]="Sequence4",e[e.Sequence5=100]="Sequence5",e[e.Sequence6=100]="Sequence6",e[e.Sequence7=100]="Sequence7"}(y||(y={})),function(e){e[e.Waiting=0]="Waiting",e[e.Dealing=1]="Dealing",e[e.Bidding=2]="Bidding",e[e.Calling=3]="Calling",e[e.Playing=4]="Playing",e[e.Finished=5]="Finished"}(p||(p={}));var q=r(784);class M{static decideBid(e){const t={hearts:0,diamonds:0,clubs:0,spades:0},r={hearts:0,diamonds:0,clubs:0,spades:0};for(const a of e.cards)t[a.color]++,r[a.color]+=w(a,a.color);let a=null,i=0;for(const e of l){const n=2*t[e]+.1*r[e];n>i&&(i=n,a=e)}if(a&&t[a]>=3&&i>=6)return a;if(e.isDealer){const e=S(Date.now());return a||l[Math.floor(e()*l.length)]}return"pass"}static decideCalling(e,t){const r=this.findAllPossibleCalls(e.cards,t);return 0===r.length?[]:(r.sort((e,t)=>t.calling.type-e.calling.type),r[0].cards)}static decideCardToPlay(e,t,r){const a=[...e.cards,...e.talon],i=a.filter(e=>B(e,t,r,a));if(0===i.length)throw new Error("No legal cards to play?");return 1===i.length?i[0]:0===t.length?this.chooseBestLeadingCard(i,r):this.chooseBestFollowingCard(i,t,r)}static findAllPossibleCalls(e,t){const r=[];for(let a=1;a<1<<e.length;a++){const i=[];for(let t=0;t<e.length;t++)a&1<<t&&i.push(e[t]);const n=I(i,t);n&&r.push({cards:i,calling:n})}return r}static chooseBestLeadingCard(e,t){const r=e.filter(e=>e.color===t);return r.length>0?r.reduce((e,r)=>w(r,t)>w(e,t)?r:e):e.reduce((e,r)=>w(r,t)>w(e,t)?r:e)}static chooseBestFollowingCard(e,t,r){const a=e.filter(e=>t.every(t=>j(e,t,r)));return a.length>0?a.reduce((e,t)=>w(t,r)<w(e,r)?t:e):e.reduce((e,t)=>w(t,r)<w(e,r)?t:e)}}let H=class extends q.EventEmitter{constructor(e){super(),this.timeoutId=null,this.options=Object.assign({endValue:501,moveTimeSec:30,botDelayMs:500,pauseBetweenRoundsMs:1e3},e),this.gameState={players:[],team1:{id:1,name:"Team 1",score:[],tricksHistory:[]},team2:{id:2,name:"Team 2",score:[],tricksHistory:[]},deck:k(),round:0,adut:null,bids:[],calls:[],gamePhase:p.Waiting,currentTrick:null,currentPlayerIndex:0,currentPlayerTimeLeft:this.options.moveTimeSec,isGameOver:!1}}createPlayer(e,t){if(this.gameState.players.length>=4)throw new Error("Cannot add more than 4 players.");if(t?.id&&this.gameState.players.some(e=>e.id===t.id))throw new Error(`Player with ID ${t.id} already exists.`);if(t?.teamId&&1!==t.teamId&&2!==t.teamId)throw new Error(`Invalid team ID: ${t.teamId}. Must be 1 or 2.`);if(t?.teamId&&this.gameState.players.filter(e=>e.teamId===t.teamId).length>=2)throw new Error(`Team ${t.teamId} is already full.`);const r=t?.isBot?"bot":"player",a=t?.isBot?"Bot":"Player",i=this.gameState.players.length+1,n={id:t?.id||`${r}-${i}`,name:e||`${a} ${i}`,teamId:t?.teamId||(this.gameState.players.length%2==0?1:2),isReady:!!t?.isBot||t?.isReady||!1,isDealer:0===this.gameState.players.length,isBot:t?.isBot||!1,cards:[],talon:[]};return this.gameState.players.push(n),this.emit("playerJoined",n),4===this.gameState.players.length&&this.gameState.players.every(e=>e.isReady)&&this.emit("allPlayersReady"),n}playerJoin(e,t){return this.createPlayer(e,t)}playerLeave(e){const t=this.gameState.players.findIndex(t=>t.id===e);if(-1===t)throw new Error(`Player with ID ${e} does not exist.`);const[r]=this.gameState.players.splice(t,1);this.emit("playerLeft",r.id),this.gameState.players.length<4&&(this.clearTimer(),this.emit("notEnoughPlayers"))}addBot(e,t){return this.createPlayer(e,{...t,isBot:!0,isReady:!0})}removeBot(e){this.playerLeave(e)}updatePlayer(e,t){const r=this.gameState.players.find(t=>t.id===e);if(!r)throw new Error(`Player with ID ${e} does not exist.`);"function"==typeof t?Object.assign(r,t(r)):Object.assign(r,t)}switchPlayerTeam(e,t){const r=this.gameState.players.find(t=>t.id===e);if(!r)throw new Error(`Player with ID ${e} does not exist.`);if(1!==t&&2!==t)throw new Error(`Team ID ${t} does not exist.`);if(r.teamId===t)throw new Error(`Player with ID ${e} is already in team ${t}.`);if(this.gameState.players.filter(e=>e.teamId===t).length>=2)throw new Error(`Team ${t} is already full.`);this.updatePlayer(e,{teamId:t}),this.emit("playerSwitchedTeam",e,t)}setPlayerReady(e,t){const r=this.gameState.players.find(t=>t.id===e);if(!r)throw new Error(`Player with ID ${e} does not exist.`);r.isBot||(this.updatePlayer(e,{isReady:t}),this.emit("playerReadyChanged",e,t),4===this.gameState.players.length&&this.gameState.players.every(e=>e.isReady)&&this.emit("allPlayersReady"))}clearTimer(){this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null)}startTimer(e,t,r){this.clearTimer(),this.gameState.currentPlayerTimeLeft=e;const a=Date.now(),i=setInterval(()=>{const t=Math.floor((Date.now()-a)/1e3);this.gameState.currentPlayerTimeLeft=Math.max(0,e-t),this.emit("timerUpdate",this.gameState.currentPlayerTimeLeft,r||null),this.gameState.currentPlayerTimeLeft<=0&&clearInterval(i)},1e3);this.timeoutId=setTimeout(()=>{clearInterval(i),t()},1e3*e)}safeBotAction(e){if(this.options.botDelayMs<=0)return e();setTimeout(()=>{try{e()}catch(e){this.emit("error",e instanceof Error?e:new Error(`Bot error: ${e}`))}},this.options.botDelayMs)}startGame(){if(this.gameState.players.length<4)throw new Error("Need 4 players to start");if(this.gameState.players.some(e=>!e.isReady))throw new Error("All players must be ready");this.gameState.round=0,this.gameState.isGameOver=!1,this.gameState.team1.score=[],this.gameState.team2.score=[],this.gameState.team1.tricksHistory=[],this.gameState.team2.tricksHistory=[],this.emit("gameStarted",this.gameState),this.startNextRound()}startNextRound(){if(this.gameState.isGameOver)return;this.gameState.round++,this.gameState.adut=null,this.gameState.bids=[],this.gameState.calls=[],this.gameState.currentTrick=null;for(const e of this.gameState.players)e.cards=[],e.talon=[];const e=this.gameState.players.findIndex(e=>e.isDealer);for(const e of this.gameState.players)this.updatePlayer(e.id,{isDealer:!1});const t=(e+1)%4,r=this.gameState.players[t];if(!r)throw new Error("No players available to be dealer.");this.updatePlayer(r.id,{isDealer:!0}),this.emit("roundStarted",this.gameState.round,this.gameState.players[t]),this.dealInitialCards()}dealInitialCards(){this.gameState.gamePhase=p.Dealing,this.gameState.deck=v(k());for(let e=0;e<2;e++)for(const e of this.gameState.players){const t=this.gameState.deck.splice(0,3);e.cards.push(...t)}this.emit("initialCardsDealt",this.gameState.players.map(e=>({playerId:e.id,cardCount:e.cards.length}))),this.startBidding()}getCurrentPlayer(){return this.gameState.players[this.gameState.currentPlayerIndex]}startBidding(){this.gameState.gamePhase=p.Bidding;const e=this.gameState.players.findIndex(e=>e.isDealer);this.gameState.currentPlayerIndex=(e+1)%4,this.emit("biddingStarted",this.getCurrentPlayer());const t=this.getCurrentPlayer();t.isBot?this.safeBotAction(()=>{const e=M.decideBid(t);this.bid(t.id,e)}):this.startTimer(this.options.moveTimeSec,()=>{this.bid(this.getCurrentPlayer().id,"pass")},t)}bid(e,t){const r=this.gameState.players.find(t=>t.id===e);if(!r)throw new Error(`Player with ID ${e} does not exist.`);if(this.gameState.gamePhase!==p.Bidding)throw new Error("Not in bidding phase.");const a=this.getCurrentPlayer();if(r.id!==a.id)throw new Error(`It's not player ${e}'s turn.`);if("pass"===t&&r.isDealer)throw new Error("Dealer cannot pass during bidding.");if(this.clearTimer(),this.gameState.bids.push({playerId:r.id,call:"pass"===t?null:t}),this.emit("bidMade",e,t),"pass"!==t)return this.gameState.adut=t,this.emit("adutChosen",t,e),void this.dealTalon();this.advanceBidding()}advanceBidding(){if(this.gameState.currentPlayerIndex=(this.gameState.currentPlayerIndex+1)%4,this.gameState.bids.filter(e=>"call"in e&&(null===e.call||"string"==typeof e.call)).length<4){this.emit("nextPlayerBid",this.getCurrentPlayer());const e=this.getCurrentPlayer();e.isBot?this.safeBotAction(()=>{const t=M.decideBid(e);this.bid(e.id,t)}):this.startTimer(this.options.moveTimeSec,()=>{const e=this.getCurrentPlayer(),t=l[Math.floor(Math.random()*l.length)];this.bid(e.id,e.isDealer?t:"pass")},e)}}dealTalon(){for(const e of this.gameState.players){const t=this.gameState.deck.splice(0,2);e.talon.push(...t)}this.emit("talonDealt",this.gameState.players.map(e=>({playerId:e.id,talon:e.talon}))),this.startCallingPhase()}startCallingPhase(){this.gameState.gamePhase=p.Calling,this.emit("callingPhaseStarted");const e=this.gameState.players.filter(e=>e.isBot),t=r=>{if(r>=e.length){const e=this.gameState.players.filter(e=>!e.isBot&&!this.gameState.calls.some(t=>t.playerId===e.id));return void(e.length>0&&this.startTimer(this.options.moveTimeSec,()=>{for(const t of e)this.gameState.calls.some(e=>e.playerId===t.id)||this.makeCall(t.id,[])}))}const a=e[r];this.gameState.adut&&this.safeBotAction(()=>{const e=[...a.cards,...a.talon],i=M.decideCalling({...a,cards:e},this.gameState.adut);this.makeCall(a.id,i),setTimeout(()=>t(r+1),1)})};if(e.length>0)t(0);else{const e=this.gameState.players.filter(e=>!e.isBot&&!this.gameState.calls.some(t=>t.playerId===e.id));e.length>0&&this.startTimer(this.options.moveTimeSec,()=>{for(const t of e)this.gameState.calls.some(e=>e.playerId===t.id)||this.makeCall(t.id,[])})}}makeCall(e,t){const r=this.gameState.players.find(t=>t.id===e);if(!r)throw new Error(`Player with ID ${e} does not exist.`);if(this.gameState.gamePhase!==p.Calling)throw new Error("Not in calling phase.");if(this.gameState.calls.some(t=>t.playerId===e))throw new Error("Player has already made a call.");if(!this.gameState.adut)throw new Error("Adut must be chosen before making calls.");this.clearTimer();const a=I(t,this.gameState.adut);if(this.gameState.calls.push({playerId:r.id,call:a?.type||y.None,cards:t}),a&&a.type===y.Belot){const a=t[0].color;this.gameState.isGameOver=!0,this.gameState.gamePhase=p.Finished;const i=1===r.teamId?this.gameState.team1:this.gameState.team2,n=this.options.endValue-i.score.reduce((e,t)=>e+t,0);return i.score.push(n),this.gameState.winnerTeam=i,this.emit("belotWin",e,a),void this.emit("gameEnded",this.gameState.winnerTeam,{team1:this.gameState.team1.score.reduce((e,t)=>e+t,0),team2:this.gameState.team2.score.reduce((e,t)=>e+t,0)},!0)}this.emit("callMade",e,t,a),4===this.gameState.calls.length&&this.resolveCalls()}resolveCalls(){const e=this.gameState.calls.filter(e=>{const t=this.gameState.players.find(t=>t.id===e.playerId);return 1===t?.teamId&&e.call!==y.None}),t=this.gameState.calls.filter(e=>{const t=this.gameState.players.find(t=>t.id===e.playerId);return 2===t?.teamId&&e.call!==y.None}),r=E(e),a=E(t);let i=null;if(r||a)if(r)if(a){const e=x(r,a);if(e>0)i=r;else if(e<0)i=a;else{const e=this.gameState.bids.find(e=>e.call===this.gameState.adut)?.playerId,t=this.gameState.players.find(t=>t.id===e)?.teamId;i=1===t?r:a}}else i=r;else i=a;else i=null;this.gameState.calls=i?[i]:[],this.emit("callingPhaseEnded",i),this.startPlayingPhase()}startPlayingPhase(){this.gameState.gamePhase=p.Playing,this.gameState.currentPlayerIndex=0,this.gameState.currentTrick={cardsPlayed:[]},this.emit("playingPhaseStarted"),this.emit("nextPlayerMove",this.getCurrentPlayer());const e=this.getCurrentPlayer();e.isBot?this.safeBotAction(()=>{if(this.gameState.adut){const t=[...e.cards,...e.talon],r=M.decideCardToPlay({...e,cards:t},this.gameState.currentTrick?.cardsPlayed||[],this.gameState.adut);this.playCard(e.id,r)}}):this.startTimer(this.options.moveTimeSec,()=>{const e=this.getCurrentPlayer();if(e.cards.length>0&&this.gameState.adut){const t=O(this.gameState.currentTrick?.cardsPlayed||[],this.gameState.adut,e.cards);t&&this.playCard(e.id,t)}},e)}playCard(e,t){const r=this.gameState.players.find(t=>t.id===e);if(!r)throw new Error(`Player with ID ${e} does not exist.`);if(this.gameState.gamePhase!==p.Playing)throw new Error("Not in playing phase.");const a=this.getCurrentPlayer();if(r.id!==a.id)throw new Error(`It's not player ${e}'s turn.`);const i=[...r.cards,...r.talon];if(-1===i.findIndex(e=>e.color===t.color&&e.type===t.type))throw new Error("Card not found in player hand.");if(!this.gameState.adut)throw new Error("Adut must be chosen before playing cards.");const n=this.gameState.currentTrick?.cardsPlayed||[];if(!B(t,n,this.gameState.adut,i))throw console.error(`Player ${e} tried to play an illegal card!`,{card:t,currentTrick:n,adut:this.gameState.adut,allPlayerCards:i}),new Error("This card cannot be played according to Belote rules.");this.clearTimer();const s=r.cards.findIndex(e=>e.color===t.color&&e.type===t.type),o=r.talon.findIndex(e=>e.color===t.color&&e.type===t.type);if(-1!==s?r.cards.splice(s,1):-1!==o&&r.talon.splice(o,1),this.gameState.currentTrick.cardsPlayed.push({playerId:r.id,...t}),this.emit("cardPlayed",e,t),4===this.gameState.currentTrick.cardsPlayed.length)this.completeTrick();else{this.gameState.currentPlayerIndex=(this.gameState.currentPlayerIndex+1)%4,this.emit("nextPlayerMove",this.getCurrentPlayer());const e=this.getCurrentPlayer();e.isBot?this.safeBotAction(()=>{if(this.gameState.adut){const t=[...e.cards,...e.talon],r=M.decideCardToPlay({...e,cards:t},this.gameState.currentTrick?.cardsPlayed||[],this.gameState.adut);this.playCard(e.id,r)}}):this.startTimer(this.options.moveTimeSec,()=>{const e=this.getCurrentPlayer();if(e.cards.length>0&&this.gameState.adut){const t=[...e.cards,...e.talon],r=O(this.gameState.currentTrick?.cardsPlayed||[],this.gameState.adut,t);r&&this.playCard(e.id,r)}},e)}}completeTrick(){const e=this.determineTrickWinner(this.gameState.currentTrick);if(this.gameState.currentTrick.winnerPlayerId=e.playerId,this.gameState.currentTrick.winningCard={color:e.color,type:e.type},(1===this.gameState.players.find(t=>t.id===e.playerId).teamId?this.gameState.team1:this.gameState.team2).tricksHistory.push(this.gameState.currentTrick),this.emit("trickCompleted",this.gameState.currentTrick,e.playerId),this.gameState.players.every(e=>0===e.cards.length&&0===e.talon.length))this.completeRound();else{this.gameState.currentPlayerIndex=this.gameState.players.findIndex(t=>t.id===e.playerId),this.gameState.currentTrick={cardsPlayed:[]},this.emit("nextTrickStarted",this.getCurrentPlayer());const t=this.getCurrentPlayer();t.isBot?this.safeBotAction(()=>{if(this.gameState.adut){const e=[...t.cards,...t.talon],r=M.decideCardToPlay({...t,cards:e},this.gameState.currentTrick?.cardsPlayed||[],this.gameState.adut);this.playCard(t.id,r)}}):this.startTimer(this.options.moveTimeSec,()=>{const e=this.getCurrentPlayer();if((e.cards.length>0||e.talon.length>0)&&this.gameState.adut){const t=[...e.cards,...e.talon],r=O(this.gameState.currentTrick?.cardsPlayed||[],this.gameState.adut,t);r&&this.playCard(e.id,r)}},t)}}determineTrickWinner(e){if(!this.gameState.adut)throw new Error("Adut must be set to determine trick winner.");let t=e.cardsPlayed[0];for(const r of e.cardsPlayed)j(r,t,this.gameState.adut)&&(t=r);return t}completeRound(){const e=this.calculateRoundScores();this.gameState.team1.score.push(e.team1),this.gameState.team2.score.push(e.team2);const t=e.team1>e.team2?this.gameState.team1:this.gameState.team2,r=this.gameState.bids.find(e=>e.call===this.gameState.adut)?.playerId,a=this.gameState.players.find(e=>e.id===r)?.teamId;let i;1===a&&0===e.team1?i=this.gameState.team1:2===a&&0===e.team2&&(i=this.gameState.team2),this.emit("roundCompleted",this.gameState.round,e,t,i);const n=this.gameState.team1.score.reduce((e,t)=>e+t,0),s=this.gameState.team2.score.reduce((e,t)=>e+t,0);n>=this.options.endValue||s>=this.options.endValue?(this.gameState.isGameOver=!0,this.gameState.gamePhase=p.Finished,this.gameState.winnerTeam=n>s?this.gameState.team1:this.gameState.team2,this.emit("gameEnded",this.gameState.winnerTeam,{team1:n,team2:s})):this.options.pauseBetweenRoundsMs>0?(this.clearTimer(),this.timeoutId=setTimeout(()=>this.startNextRound(),this.options.pauseBetweenRoundsMs)):this.startNextRound()}calculateRoundScores(){let e=0,t=0;for(const r of[this.gameState.team1,this.gameState.team2]){let a=0;for(const e of r.tricksHistory)for(const t of e.cardsPlayed)a+=w(t,this.gameState.adut);if(r.tricksHistory.length>0){const e=r.tricksHistory[r.tricksHistory.length-1],t=this.getLastTrickOverall();t&&this.areTricksEqual(e,t)&&(a+=20)}8===r.tricksHistory.length&&(a+=90),1===r.id?e=a:t=a}const r=this.gameState.calls.reduce((e,t)=>e+t.call,0),a=162+r,i=Math.floor(a/2)+1,n=this.gameState.bids.find(e=>e.call===this.gameState.adut)?.playerId,s=this.gameState.players.find(e=>e.id===n)?.teamId;switch(s){case 1:e>=i?e+=r:(t=a,e=0);break;case 2:t>=i?t+=r:(e=a,t=0);break;default:e+=r,t+=r}return{team1:e,team2:t}}getLastTrickOverall(){const e=[...this.gameState.team1.tricksHistory,...this.gameState.team2.tricksHistory];return e.length>0?e[e.length-1]:null}areTricksEqual(e,t){for(let r=0;r<e.cardsPlayed.length;r++){const a=e.cardsPlayed[r],i=t.cardsPlayed[r];if(a.playerId!==i.playerId||a.color!==i.color||a.type!==i.type)return!1}return!0}getPlayerById(e){return this.gameState.players.find(t=>t.id===e)||null}getPlayersByTeam(e){return this.gameState.players.filter(t=>t.teamId===e)}destroy(){this.clearTimer(),this.removeAllListeners()}};H=function(e,t,r,a){var i,n=arguments.length,s=n<3?t:null===a?a=Object.getOwnPropertyDescriptor(t,r):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,a);else for(var o=e.length-1;o>=0;o--)(i=e[o])&&(s=(n<3?i(s):n>3?i(t,r,s):i(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s}([function(e){const t=e.prototype;return Object.getOwnPropertyNames(t).forEach(e=>{if("constructor"===e)return;const r=Object.getOwnPropertyDescriptor(t,e);r&&"function"==typeof r.value&&!e.startsWith("_")&&(function(e,t,r){const a=r.value;r.value=function(...e){try{const r=a.apply(this,e);return r&&"object"==typeof r&&null!==r&&"catch"in r&&"function"==typeof r.catch?r.catch(e=>{throw this.emit&&"function"==typeof this.emit&&this.emit("error",e instanceof Error?e:new Error(`${String(t)}: ${String(e)}`)),e}):r}catch(e){throw this.emit&&"function"==typeof this.emit&&this.emit("error",e instanceof Error?e:new Error(`${String(t)}: ${String(e)}`)),e}}}(0,e,r),Object.defineProperty(t,e,r))}),e},function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}("design:paramtypes",[Object])],H);const J={...o,...n,...i,...s};return a.default})());