# Solver Solver try to remember what cards are played before and what was the previous moves of the opponent. this is the 2nd version not finished yet (and it is a big mess now) to make it work on node 10 add `competitors.append(Player("Solver", ["node", "--experimental-modules", "./solver.mjs"]))` if node 12 `competitors.append(Player("Solver", ["node", "./solver.js"]))` be carefuller with the file type ```lang-js import fs from 'fs' import { promisify } from 'util' const appendFile = promisify(fs.appendFile) const writeFile = promisify(fs.writeFile) const readFile = promisify(fs.readFile) const delay = ms => new Promise(_ => setTimeout(_, ms)); let [filename, othercoins, mycoins, mycards ] = process.argv.slice(2) othercoins = +othercoins mycoins = +mycoins const move = async m => await appendFile(filename, m) const message = m => process.stdout.write(m) const endTurn = async _ => await move(`\n`) const stateFileName = `./state.json` const defaultState = { inTheDeck: [], oponentCards: [], oponentMissingTempCard: "", oponentMissingCards: [], oponentDropedCards: [], oponentCardModifier: 0 } const CardTypes = Object.freeze({ Ambassador : `Ambassador`, Assassin : `Assassin`, Captain : `Captain`, Contessa : `Contessa`, Duke : `Duke`, }) const revealTable = Object.freeze({ [CardTypes.Ambassador]: `~`, [CardTypes.Assassin]: `^`, [CardTypes.Captain]: `*`, [CardTypes.Contessa]: `!`, [CardTypes.Duke]: `$`, }) const giveUpTable = Object.freeze({ [CardTypes.Ambassador]: `_`, [CardTypes.Assassin]: `'`, [CardTypes.Captain]: `<`, [CardTypes.Contessa]: `=`, [CardTypes.Duke]: `0`, }) function GetRevealCardChar(cardType) { return revealTable[cardType] } function GetRevealCardType(char) { return Object.keys(revealTable).find(key => revealTable[key] === char) } function GetGiveUpCardChar(cardType) { return giveUpTable[cardType] } function GetGiveUpCardType(char) { return Object.keys(giveUpTable).find(key => giveUpTable[key] === char) } async function GiveUpCard(cardType, endTurn = false) { return await move( GetGiveUpCardChar(cardType) + `${endTurn?`\n`:``}` ); } function OponentCanHave(cardType) { // it has it if (!!~state.oponentCards.indexOf(cardType)) return true if (state.oponentCards.length + state.oponentDropedCards.length >= 2) return false return true } function GiveCard(getOrder = false) { // TODO: Tactic const giveAwayOrder = [ CardTypes.Captain, CardTypes.Ambassador, CardTypes.Contessa, CardTypes.Assassin, CardTypes.Duke, ] const tmp = mycards .split('') .map(GetRevealCardType) let [unique, duplicate] = tmp.reduce(([unique, duplicate], item) => { return unique.includes(item) ? [unique, [...duplicate, item]] : [[...unique, item], duplicate] } , [[],[]]) unique.sort( (a, b) => giveAwayOrder.indexOf(a) - giveAwayOrder.indexOf(b)); duplicate.sort( (a, b) => giveAwayOrder.indexOf(a) - giveAwayOrder.indexOf(b)) const out = [...duplicate, ...unique] return getOrder? out: GetGiveUpCardChar(out[0]); } const iHaveAmbassador = !!~mycards.indexOf(`~`) const iHaveAssassin = !!~mycards.indexOf(`^`) const iHaveCaptain = !!~mycards.indexOf(`*`) const iHaveContessa = !!~mycards.indexOf(`!`) const iHaveDuke = !!~mycards.indexOf(`$`) let state = defaultState ;(async ()=>{ const data = (await readFile(filename, `utf8`)).replace(/\r/g, ``) const isNewGame = data === "" && mycoins === 1 && othercoins === 1 if (isNewGame) { await writeFile(stateFileName, JSON.stringify(state)) } else { state = JSON.parse(await readFile(stateFileName, `utf8`)) } //console.error(state); let line = data.split(/\n/g).pop() // I am in the exchnage if (mycards.length >= 3) { const [c1, c2] = GiveCard(true).reverse() if (mycards.length === 3) { state.inTheDeck.push(c1) message(GetRevealCardChar(c1)) } if (mycards.length === 4) { state.inTheDeck.push(c1, c2) message(`${GetRevealCardChar(c1)}${GetRevealCardChar(c2)}`) } return await move(`\n`) } const newTurn = line === `` if (newTurn) { if (mycoins >= 7) { return await move(`C`) } if (othercoins >= 6) { if (iHaveCaptain) return await move(`S`) if (mycoins <= 6 && mycards.length <= 1) { // TODO: bluff } } if ( !iHaveDuke && !iHaveContessa && iHaveAmbassador ) { return await move(`E`) } // Assasinate if oponent has no Contessa if ( mycoins >= 3 && iHaveAssassin && !~state.oponentCards.indexOf(CardTypes.Contessa) ) { return await move(`A`) } if (iHaveDuke) { return await move(`T`) } return await move(`I\n`) } // Exchange if (line === `Eq`) { if (iHaveAmbassador) return await move(GetRevealCardChar(CardTypes.Ambassador)) return await GiveUpCard(GiveCard(true)[0]) } // Tax Challenge if(line === `Tq`) { if (iHaveDuke) return await move(GetRevealCardChar(CardTypes.Duke)) return await GiveUpCard(GiveCard(true)[0]) } if (line === `Sa`) { if (!~state.oponentCards.indexOf(CardTypes.Ambassador)) { state.oponentMissingTempCard = CardTypes.Ambassador return await move(`q`) } return await endTurn() } if (line === `Sc`) { if (!~state.oponentCards.indexOf(CardTypes.Captain)) { state.oponentMissingTempCard = CardTypes.Captain return await move(`q`) } return await endTurn() } if (line=== `Saq${GetRevealCardChar(CardTypes.Ambassador)}`) { state.oponentMissingTempCard = `` state.oponentCards.push( CardTypes.Ambassador ); return await GiveUpCard(GiveCard(true)[0], true) } if (line=== `Scq${GetRevealCardChar(CardTypes.Captain)}`) { state.oponentMissingTempCard = `` state.oponentCards.push( CardTypes.Captain ); return await GiveUpCard(GiveCard(true)[0], true) } if (line === `Sq`) { if (iHaveCaptain) return await move(GetRevealCardChar(CardTypes.Captain)) return await GiveUpCard(GiveCard(true)[0]) } // Assassinate Block and Chalange it if (line === `As`) { state.oponentMissingTempCard = CardTypes.Contessa return await move(`q`) } // Assasint blocked by Contessa if (line === `Asq${GetRevealCardChar(CardTypes.Contessa)}`) { state.oponentMissingTempCard = `` state.oponentCards.push( CardTypes.Contessa ) // Assassin useless here lets give it up return await GiveUpCard(CardTypes.Assassin, true) } // Assassinate challenge if (line === `Aq`) { if (iHaveAssassin) return await move(GetRevealCardChar(CardTypes.Assassin)) return await GiveUpCard(GiveCard(true)[0]) } // Defense Moves if (line === `C`) { return await GiveUpCard(GiveCard(true)[0]) } if (line === `A`) { if (iHaveContessa) return await move(`s`) if (!!~state.oponentCards.indexOf(CardTypes.Assassin)) { // If oponent has an Assasin card lets bluff return await move(`s`) } else { state.oponentMissingTempCard = CardTypes.Assassin return await move(`q`) } } if (line === `Aq${GetRevealCardChar(CardTypes.Assassin)}`) { state.oponentMissingTempCard = `` state.oponentCards.push( CardTypes.Assassin ); return await GiveUpCard(GiveCard(true)[0], true) } if (line === `Asq`) { if (iHaveContessa) return await move(GetRevealCardChar(CardTypes.Contessa)) return await GiveUpCard(GiveCard(true)[0]) } if (line === `S`) { if (iHaveAmbassador) return await move(`a`) if (iHaveCaptain) return await move(`c`) return await move(`p`) } if (line === `Saq`) { if (iHaveAmbassador) return await move(GetRevealCardChar(CardTypes.Ambassador)) return await GiveUpCard(GiveCard(true)[0]) } if (line === `Scq`) { if (iHaveCaptain) return await move(GetRevealCardChar(CardTypes.Captain)) return await GiveUpCard(GiveCard(true)[0]) } if (line === `F`) { if (iHaveDuke) return await move(`d`) return await move(`p`) } if (line === `Fdq`) { if (iHaveDuke) return await move(GetRevealCardChar(CardTypes.Duke)) return await GiveUpCard(GiveCard(true)[0]) } if (line === `E`) { if (!OponentCanHave(CardTypes.Ambassador)) { return await move(`q`) } state.oponentCards = [] state.oponentMissingCards = [] state.oponentMissingTempCard = '' return await move(`p`) } if (line === `Eq${GetRevealCardChar(CardTypes.Ambassador)}`) { console.error(111, `THIS SHOULD NEVER HAPPEN`) return await GiveUpCard(GiveCard(true)[0]) } if (line === `T`) { if (!OponentCanHave(CardTypes.Duke)) { return await move(`q`) } return await move(`p`) } if (line === `Tq${GetRevealCardChar(CardTypes.Duke)}`) { console.error(111, `THIS SHOULD NEVER HAPPEN`) return await GiveUpCard(GiveCard(true)[0]) } // remove oponents drop card from the state // can't detect if oponent has the same card twice if (!!~Object.values(giveUpTable).indexOf(line.substr(-1))) { // Catch the bluff if (state.oponentMissingTempCard !== "") { state.oponentMissingCards.push(state.oponentMissingTempCard) state.oponentMissingTempCard = "" } // maybe we should asume user doeas not has the same card? const cardType = GetGiveUpCardType(line.substr(-1)) state.oponentCards.filter(c => c !== cardType) state.inTheDeck.push(cardType) state.oponentDropedCards.push(cardType) } return await endTurn() })() .then(async () => { await writeFile(stateFileName, JSON.stringify(state)) }) .catch(console.error.bind(console)) ```