From 2788265efcf4448680877b1a1a8492bebbec6480 Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Sun, 26 Jun 2022 11:14:52 -0700 Subject: [PATCH] Implementing more admin commands Signed-off-by: James Ketrenos --- server/ai/app.js | 85 ++++++++++++++++++++++++++++------- server/ai/longest-road.js | 2 +- server/routes/games.js | 60 +++---------------------- server/rules | 34 ++++++++++++++ server/util/layout.js | 59 +++++++++++++++++++++++- server/util/validLocations.js | 2 +- 6 files changed, 168 insertions(+), 74 deletions(-) create mode 100755 server/rules diff --git a/server/ai/app.js b/server/ai/app.js index 9768cf9..d8b9035 100644 --- a/server/ai/app.js +++ b/server/ai/app.js @@ -4,7 +4,7 @@ const fs = require('fs').promises; const calculateLongestRoad = require('./longest-road.js'); const { getValidRoads, getValidCorners } = require('../util/validLocations.js'); -const layout = require('../util/layout.js'); +const { layout, staticData } = require('../util/layout.js'); const version = '0.0.1'; @@ -206,26 +206,68 @@ const sleep = async (delay) => { }); }; + +const bestSettlementPlacement = (game) => { + const best = { + index: -1, + pips: 0 + }; + + /* For each corner that is valid, find out which + * tiles are on that corner, and for each of those + * tiles, find the pip placement for that tile. */ + game.turn.limits.corners.forEach(cornerIndex => { + const tiles = []; + layout.tiles.forEach((tile, index) => { + if (tile.corners.indexOf(cornerIndex) !== -1 + && tiles.indexOf(index) === -1) { + tiles.push({ tile, index }); + } + }); + + let cornerScore = 0; + + /* Find the tileOrder holding this tile */ + tiles.forEach(tile => { + const index = tile.index; +// const tileIndex = game.tileOrder.indexOf(index); + const pipIndex = game.pipOrder[index]; + const score = staticData.pips[pipIndex].pips; + cornerScore += score; + }); + + if (cornerScore > best.pips) { + best.index = cornerIndex; + best.pips = cornerScore; + } + }); + + console.log(`${name} - Corner ${best.index} gives ${best.pips} pips.`); + return best.index; +} + const bestRoadPlacement = (game) => { const road = calculateLongestRoad(game); console.log(`${name} - could make road ${road.segments + 1} long on ${road.index}`); let attempt = -1; - layout.roads[road.index].corners.forEach(cornerIndex => { - if (attempt !== -1) { - return; - } - layout.corners[cornerIndex].roads.forEach(roadIndex => { + if (road.index !== -1) { + layout.roads[road.index].corners.forEach(cornerIndex => { if (attempt !== -1) { return; } - const placedRoad = game.placements.roads[roadIndex]; - if (placedRoad.color) { - return; - } - attempt = roadIndex; + layout.corners[cornerIndex].roads.forEach(roadIndex => { + if (attempt !== -1) { + return; + } + const placedRoad = game.placements.roads[roadIndex]; + if (placedRoad.color) { + return; + } + attempt = roadIndex; + }); }); - }); + } if (game.turn.limits.roads.indexOf(attempt) !== -1) { console.log(`${name} - attempting to place on end of longest road`); @@ -331,6 +373,18 @@ const processGameOrder = async () => { }; const processInitialPlacement = async (received) => { + /* Fetch the various game order elements so we can make + * educated location selections */ + if (!game.pipOrder + || !game.tileOrder + || !game.borderOrder) { + return { + pipOrder: anyValue, + tileOrder: anyValue, + borderOrder: anyValue + } + } + if (!game.turn || game.turn.color !== game.color) { return { turn: { @@ -363,8 +417,7 @@ const processInitialPlacement = async (received) => { if (type === 'place-road') { index = bestRoadPlacement(game); } else if (type === 'place-settlement') { - index = game.turn.limits.corners[Math.floor( - Math.random() * game.turn.limits.corners.length)]; + index = bestSettlementPlacement(game); } console.log(`Selecting ${type} at ${index}`); send({ @@ -626,9 +679,7 @@ const processNormal = async (received) => { } if (game.turn.actions && game.turn.actions.indexOf('place-settlement') !== -1) { - console.log({ corners: game.turn.limits.corners }); - index = game.turn.limits.corners[Math.floor( - Math.random() * game.turn.limits.corners.length)]; + const index = bestSettlementPlacement(game); send({ type: 'place-settlement', index }); diff --git a/server/ai/longest-road.js b/server/ai/longest-road.js index d890987..317fc8c 100644 --- a/server/ai/longest-road.js +++ b/server/ai/longest-road.js @@ -1,4 +1,4 @@ -const layout = require('../util/layout.js'); +const { layout } = require('../util/layout.js'); const processCorner = (game, color, cornerIndex, placedCorner) => { /* If this corner is allocated and isn't assigned to the walking color, skip it */ diff --git a/server/routes/games.js b/server/routes/games.js index ad577ed..ef8e11d 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -8,7 +8,7 @@ const express = require("express"), accessSync = fs.accessSync, randomWords = require("random-words"), equal = require("fast-deep-equal"); -const layout = require('../util/layout.js'); +const { layout, staticData } = require('../util/layout.js'); const { getValidRoads, getValidCorners, isRuleEnabled } = require('../util/validLocations.js'); @@ -51,58 +51,6 @@ function shuffleArray(array) { return array; } -const staticData = { - tiles: [ - { type: "desert", card: 0 }, - { type: "wood", card: 0 }, - { type: "wood", card: 1 }, - { type: "wood", card: 2 }, - { type: "wood", card: 3 }, - { type: "wheat", card: 0 }, - { type: "wheat", card: 1 }, - { type: "wheat", card: 2 }, - { type: "wheat", card: 3 }, - { type: "stone", card: 0 }, - { type: "stone", card: 1 }, - { type: "stone", card: 2 }, - { type: "sheep", card: 0 }, - { type: "sheep", card: 1 }, - { type: "sheep", card: 2 }, - { type: "sheep", card: 3 }, - { type: "brick", card: 0 }, - { type: "brick", card: 1 }, - { type: "brick", card: 2 } - ], - pips: [ - { roll: 5, pips: 4 }, - { roll: 2, pips: 1 }, - { roll: 6, pips: 5 }, - { roll: 3, pips: 2 }, - { roll: 8, pips: 5 }, - { roll: 10, pips: 3 }, - { roll: 9, pips: 4 }, - { roll: 12, pips: 1 }, - { roll: 11, pips: 2 }, - { roll: 4, pips: 3 }, - { roll: 8, pips: 5 }, - { roll: 10, pips: 3 }, - { roll: 9, pips: 4 }, - { roll: 4, pips: 3 }, - { roll: 5, pips: 4 }, - { roll: 6, pips: 6 }, - { roll: 3, pips: 2 }, - { roll: 11, pips: 2 }, - { roll: 7, pips: 0 }, /* Robber is at the end or indexing gets off */ - ], - borders: [ - [ "bank", undefined, "sheep" ], - [ undefined, "bank", undefined ], - [ "bank", undefined, "brick" ], - [ undefined, "wood", undefined ], - [ "bank", undefined, "wheat" ], - [ undefined, "stone", undefined ] - ] -}; const games = {}; const audio = {}; @@ -759,6 +707,12 @@ const adminCommands = (game, action, value, query) => { let color, player, parts, session, corners, error; switch (action) { + case 'rules': + const rule = value.replace(/^=.*/, ''); + let values = value.replace(/^.*=/, '').split(','); + console.log(rule, values); + break; + case "debug": if (parseInt(value) === 0 || value === 'false') { delete game.debug; diff --git a/server/rules b/server/rules new file mode 100755 index 0000000..98e1ab1 --- /dev/null +++ b/server/rules @@ -0,0 +1,34 @@ +#!/bin/bash +ADMIN=$(jq -r .admin config/local.json) +if [[ "${ADMIN}" == "" ]]; then + echo "You need to set your { 'admin': 'secret' } in config/local.json" + exit 1 +fi + +id=$1 +shift +rule=$1 +shift +params="${*}" +params="${params// /,}" + +if [[ "${id}" == "" ]] || [[ "${rule}" == "" ]] || [[ "${params}" == "" ]]; then +cat << EOF +Usage: rules GAME-ID RULE KEY:VALUE [KEY:VALUE] + +Examples: + + ./rules test volcano enabled:true gold:true number:4 + +EOF + exit 1 +fi + +curl --noproxy '*' -s -L \ + --request PUT \ + --header "PRIVATE-TOKEN: ${ADMIN}" \ + --header "Content-Type: application/json" \ + http://localhost:8930/ketr.ketran/api/v1/games/${id}/rules/${rule}=${params} +# jq -r .status + + diff --git a/server/util/layout.js b/server/util/layout.js index d870d11..2a426e7 100644 --- a/server/util/layout.js +++ b/server/util/layout.js @@ -1,6 +1,5 @@ "use strict"; - /* Board Tiles: * 0 1 2 * 3 4 5 6 @@ -262,4 +261,60 @@ const layout = { ] }; -module.exports = layout; +const staticData = { + tiles: [ + { type: "desert", card: 0 }, + { type: "wood", card: 0 }, + { type: "wood", card: 1 }, + { type: "wood", card: 2 }, + { type: "wood", card: 3 }, + { type: "wheat", card: 0 }, + { type: "wheat", card: 1 }, + { type: "wheat", card: 2 }, + { type: "wheat", card: 3 }, + { type: "stone", card: 0 }, + { type: "stone", card: 1 }, + { type: "stone", card: 2 }, + { type: "sheep", card: 0 }, + { type: "sheep", card: 1 }, + { type: "sheep", card: 2 }, + { type: "sheep", card: 3 }, + { type: "brick", card: 0 }, + { type: "brick", card: 1 }, + { type: "brick", card: 2 } + ], + pips: [ + { roll: 5, pips: 4 }, + { roll: 2, pips: 1 }, + { roll: 6, pips: 5 }, + { roll: 3, pips: 2 }, + { roll: 8, pips: 5 }, + { roll: 10, pips: 3 }, + { roll: 9, pips: 4 }, + { roll: 12, pips: 1 }, + { roll: 11, pips: 2 }, + { roll: 4, pips: 3 }, + { roll: 8, pips: 5 }, + { roll: 10, pips: 3 }, + { roll: 9, pips: 4 }, + { roll: 4, pips: 3 }, + { roll: 5, pips: 4 }, + { roll: 6, pips: 5 }, + { roll: 3, pips: 2 }, + { roll: 11, pips: 2 }, + { roll: 7, pips: 0 }, /* Robber is at the end or indexing gets off */ + ], + borders: [ + ["bank", undefined, "sheep"], + [undefined, "bank", undefined], + ["bank", undefined, "brick"], + [undefined, "wood", undefined], + ["bank", undefined, "wheat"], + [undefined, "stone", undefined] + ] +}; + +module.exports = { + layout, + staticData +}; diff --git a/server/util/validLocations.js b/server/util/validLocations.js index 1b65394..c93c5f3 100644 --- a/server/util/validLocations.js +++ b/server/util/validLocations.js @@ -1,4 +1,4 @@ -const layout = require('./layout.js'); +const { layout } = require('./layout.js'); const isRuleEnabled = (game, rule) => { return rule in game.rules && game.rules[rule].enabled;