diff --git a/.gitignore b/.gitignore index b512c09..ecb673b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -node_modules \ No newline at end of file +node_modules + +coverage +coverage-html \ No newline at end of file diff --git a/README.md b/README.md index 2f6d5ce..2cc0d6a 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,15 @@ http://2ndpinew.site/d/test/kakomimasu/local/v0/?w=10&h=10&nAgent=6&endTurn=10&p import { Action, Board, - Kakomimasu, -} from "https://raw.githubusercontent.com/codeforkosen/Kakomimasu/v1.0.0/Kakomimasu.js"; -const kkmm = new Kakomimasu(); + Game, + Player, +} from "https://raw.githubusercontent.com/codeforkosen/Kakomimasu/${version}/mod.ts"; +const board: Board = { + width: 10, + height: 10, + points: new Array(width * height), +}; +const game = new Game(board); ``` ## コア利用方法(リポジトリを取得し、ローカルで使用する) @@ -80,17 +86,16 @@ $ deno run sample/main.js ## コアテスト ```console -$ deno test ./test +$ deno task test ``` -Github -Actionsのローカル実行ツール「[act](https://github.com/nektos/act)」を使用したテストを行うには、actとDockerをインストールした状態で以下を実行 +## その他 -```console -$ act -``` +### Online版 -## その他 +Kakomimasuコアを利用したAPIを通してリアルタイム対戦可能なオンラインサーバが公開されています。 + +詳しくはこちら:[kakomimasu.com](https://kakomimasu.com) ### スマホアプリデザイン案 diff --git a/cjs/Kakomimasu.js b/cjs/Kakomimasu.js index 618b19e..7354349 100644 --- a/cjs/Kakomimasu.js +++ b/cjs/Kakomimasu.js @@ -1,119 +1,40 @@ "use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var _Agent_instances, _Agent_lastaction, _Agent_isOnBoard, _Agent_checkOnBoard, _Agent_checkDir, _Agent_checkPut, _Agent_checkMove, _Agent_checkRemove, _Agent_put, _Agent_move, _Game_instances, _Game_checkActions, _Game_checkConflict, _Game_putOrMove, _Game_revertOverlap, _Game_removeOrNot, _Game_revertNotOwnerWall, _Game_commit; Object.defineProperty(exports, "__esModule", { value: true }); -exports.Player = exports.Kakomimasu = exports.Game = exports.Field = exports.Board = exports.Agent = exports.Action = void 0; +exports.Player = exports.Game = exports.Field = exports.Agent = exports.Action = void 0; const util_js_1 = require("./util.js"); -class Board { - constructor({ w, h, points, nagent, nturn, nsec, nplayer, name }) { - Object.defineProperty(this, "w", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "h", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "points", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nagent", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nturn", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nsec", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nplayer", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "name", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - this.w = w; - this.h = h; - this.points = points; - this.nagent = nagent; - this.nturn = nturn || 30; - this.nsec = nsec || 3; - this.nplayer = nplayer || 2; - this.name = name || ""; - if (this.points.length !== this.w * this.h) { - throw new Error("points.length must be " + this.w * this.h); - } - //console.log("board", this, this.name); - // if (!(w >= 12 && w <= 24 && h >= 12 && h <= 24)) { throw new Error("w and h 12-24"); } - } - static restore(data) { - const board = new Board(data); - return board; - } - toLogJSON() { - return Object.assign({}, this); - } - getJSON() { - return { - name: this.name, - w: this.w, - h: this.h, - points: this.points, - nagents: this.nagent, - nturn: this.nturn, - nsec: this.nsec, - nplayer: this.nplayer, - }; - } - toJSON() { - return { - name: this.name, - width: this.w, - height: this.h, - nAgent: this.nagent, - nPlayer: this.nplayer, - nTurn: this.nturn, - nSec: this.nsec, - points: this.points, - }; - } -} -exports.Board = Board; class Agent { - constructor(board, field, playerid) { - Object.defineProperty(this, "board", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); + constructor(field, playeridx) { + _Agent_instances.add(this); Object.defineProperty(this, "field", { enumerable: true, configurable: true, writable: true, value: void 0 }); - Object.defineProperty(this, "playerid", { + Object.defineProperty(this, "playerIdx", { enumerable: true, configurable: true, writable: true, @@ -143,172 +64,141 @@ class Agent { writable: true, value: void 0 }); - Object.defineProperty(this, "lastaction", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - this.board = board; + _Agent_lastaction.set(this, void 0); this.field = field; - this.playerid = playerid; + this.playerIdx = playeridx; this.x = -1; this.y = -1; this.bkx = -1; this.bky = -1; - this.lastaction = null; + __classPrivateFieldSet(this, _Agent_lastaction, null, "f"); } - static restore(data, board, field) { - var _a; - const agent = new Agent(board, field, data.playerid); + static fromJSON(data, playerIdx, field) { + const agent = new Agent(field, playerIdx); agent.x = data.x; agent.y = data.y; - agent.bkx = data.bkx; - agent.bky = data.bky; - agent.lastaction = (_a = data.lastaction) !== null && _a !== void 0 ? _a : null; return agent; } - toLogJSON() { - return Object.assign(Object.assign({}, this), { board: null, field: null }); - } - isOnBoard() { - return this.x !== -1; - } - checkOnBoard(x, y) { - return x >= 0 && x < this.board.w && y >= 0 && y < this.board.h; - } - checkDir(x, y) { - if (this.x === x && this.y === y) - return false; - return Math.abs(this.x - x) <= 1 && Math.abs(this.y - y) <= 1; + toJSON() { + return { x: this.x, y: this.y }; } check(act) { - this.lastaction = act; + __classPrivateFieldSet(this, _Agent_lastaction, act, "f"); + this.bkx = this.x; + this.bky = this.y; const x = act.x; const y = act.y; const t = act.type; if (t === Action.PUT) - return this.checkPut(x, y); - if (t === Action.NONE) - return this.checkNone(x, y); - if (t === Action.MOVE) - return this.checkMove(x, y); - if (t === Action.REMOVE) - return this.checkRemove(x, y); - return false; - } - checkPut(x, y) { - if (this.isOnBoard()) - return false; - if (!this.checkOnBoard(x, y)) - return false; - return true; - } - checkNone(_x, _y) { - if (!this.isOnBoard()) - return false; - return true; - } - checkMove(x, y) { - if (!this.isOnBoard()) - return false; - if (!this.checkOnBoard(x, y)) - return false; - if (!this.checkDir(x, y)) - return false; - return true; - } - checkRemove(x, y) { - if (!this.isOnBoard()) - return false; - if (!this.checkOnBoard(x, y)) - return false; - if (!this.checkDir(x, y)) - return false; - //const _n = x + y * this.board.w; - if (this.field.get(x, y).type !== Field.WALL) + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkPut).call(this, x, y); + else if (t === Action.MOVE) + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkMove).call(this, x, y); + else if (t === Action.REMOVE) + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkRemove).call(this, x, y); + else return false; - return true; } isValidAction() { - if (!this.lastaction) - return false; - if (this.lastaction.res !== Action.SUCCESS) - return false; - return true; + if (!__classPrivateFieldGet(this, _Agent_lastaction, "f")) + return; + if (__classPrivateFieldGet(this, _Agent_lastaction, "f").res !== Action.SUCCESS) + return; + return __classPrivateFieldGet(this, _Agent_lastaction, "f"); } putOrMove() { //console.log("putormove", this); - if (this.lastaction == null) + if (__classPrivateFieldGet(this, _Agent_lastaction, "f") == null) throw new Error("putOrMove before check"); - if (this.lastaction.res !== Action.SUCCESS) + if (__classPrivateFieldGet(this, _Agent_lastaction, "f").res !== Action.SUCCESS) return false; - const act = this.lastaction; + const act = __classPrivateFieldGet(this, _Agent_lastaction, "f"); const x = act.x; const y = act.y; const t = act.type; if (t === Action.PUT) - return this.put(x, y); + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_put).call(this, x, y); if (t === Action.MOVE) - return this.move(x, y); - return true; - } - put(x, y) { - if (!this.checkPut(x, y)) - return false; - if (!this.field.setAgent(this.playerid, x, y)) { - return false; // throw new Error("can't enter the wall"); - } - this.x = x; - this.y = y; - return true; - } - none(x, y) { - if (!this.checkNone(x, y)) - return false; - return true; - } - move(x, y) { - if (!this.checkMove(x, y)) - return false; - if (!this.field.setAgent(this.playerid, x, y)) { - return false; // throw new Error("can't enter the wall"); - } - this.x = x; - this.y = y; + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_move).call(this, x, y); return true; } remove() { - if (this.lastaction == null) + if (__classPrivateFieldGet(this, _Agent_lastaction, "f") == null) throw new Error("remove before check"); - const { x, y } = this.lastaction; - if (!this.checkRemove(x, y)) + const { x, y } = __classPrivateFieldGet(this, _Agent_lastaction, "f"); + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkRemove).call(this, x, y)) return false; this.field.set(x, y, Field.AREA, null); return true; } commit() { - this.bkx = this.x; - this.bky = this.y; - this.lastaction = null; + __classPrivateFieldSet(this, _Agent_lastaction, null, "f"); } revert() { - this.x = this.bkx; - this.y = this.bky; - const act = this.lastaction; + const act = __classPrivateFieldGet(this, _Agent_lastaction, "f"); if (act && (act.type === Action.MOVE || act.type === Action.PUT) && act.res === Action.SUCCESS) { act.res = Action.REVERT; + this.x = this.bkx; + this.y = this.bky; } } - getJSON() { - return { x: this.x, y: this.y }; - } } exports.Agent = Agent; +_Agent_lastaction = new WeakMap(), _Agent_instances = new WeakSet(), _Agent_isOnBoard = function _Agent_isOnBoard() { + return this.x !== -1; +}, _Agent_checkOnBoard = function _Agent_checkOnBoard(x, y) { + return x >= 0 && x < this.field.width && y >= 0 && + y < this.field.height; +}, _Agent_checkDir = function _Agent_checkDir(x, y) { + if (this.x === x && this.y === y) + return false; + return Math.abs(this.x - x) <= 1 && Math.abs(this.y - y) <= 1; +}, _Agent_checkPut = function _Agent_checkPut(x, y) { + if (__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_isOnBoard).call(this)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkOnBoard).call(this, x, y)) + return false; + return true; +}, _Agent_checkMove = function _Agent_checkMove(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_isOnBoard).call(this)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkOnBoard).call(this, x, y)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkDir).call(this, x, y)) + return false; + return true; +}, _Agent_checkRemove = function _Agent_checkRemove(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_isOnBoard).call(this)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkOnBoard).call(this, x, y)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkDir).call(this, x, y)) + return false; + if (this.field.get(x, y).type !== Field.WALL) + return false; + return true; +}, _Agent_put = function _Agent_put(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkPut).call(this, x, y)) + return false; + if (!this.field.setAgent(this.playerIdx, x, y)) { + return false; // throw new Error("can't enter the wall"); + } + this.x = x; + this.y = y; + return true; +}, _Agent_move = function _Agent_move(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkMove).call(this, x, y)) + return false; + if (!this.field.setAgent(this.playerIdx, x, y)) { + return false; // throw new Error("can't enter the wall"); + } + this.x = x; + this.y = y; + return true; +}; class Action { constructor(agentid, type, x, y) { - Object.defineProperty(this, "agentid", { + Object.defineProperty(this, "agentId", { enumerable: true, configurable: true, writable: true, @@ -338,26 +228,17 @@ class Action { writable: true, value: void 0 }); - this.agentid = agentid; + this.agentId = agentid; this.type = type; this.x = x; this.y = y; this.res = Action.SUCCESS; } - static restore(data) { - const action = new Action(data.agentid, data.type, data.x, data.y); + static fromJSON(data) { + const action = new Action(data.agentId, data.type, data.x, data.y); action.res = data.res; return action; } - getJSON() { - return { - agentId: this.agentid, - type: this.type, - x: this.x, - y: this.y, - res: this.res, - }; - } static getMessage(res) { return [ "success", @@ -432,52 +313,77 @@ Object.defineProperty(Action, "ERR_ILLEGAL_ACTION", { writable: true, value: 5 }); -Object.defineProperty(Action, "fromJSON", { +Object.defineProperty(Action, "fromArray", { enumerable: true, configurable: true, writable: true, value: (array) => array.map((a) => new Action(a[0], a[1], a[2], a[3])) }); class Field { - constructor(board) { - Object.defineProperty(this, "board", { + constructor({ width, height, points, nAgent = 4, nPlayer = 2 }) { + Object.defineProperty(this, "width", { enumerable: true, configurable: true, writable: true, value: void 0 }); - Object.defineProperty(this, "field", { + Object.defineProperty(this, "height", { enumerable: true, configurable: true, writable: true, value: void 0 }); - this.board = board; - // field - this.field = []; - for (let i = 0; i < this.board.w * this.board.h; i++) { - this.field.push({ type: Field.AREA, player: null }); + Object.defineProperty(this, "nAgent", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "nPlayer", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "points", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "tiles", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + if (points.length !== width * height) { + throw Error("points.length must be " + width * height); } + this.width = width; + this.height = height; + this.nAgent = nAgent; + this.nPlayer = nPlayer; + this.points = points; + this.tiles = new Array(width * height).fill({ + type: Field.AREA, + player: null, + }); } - static restore(data, board) { - const field = new Field(board); - field.field = data.field; + static fromJSON(data) { + const { tiles } = data, init = __rest(data, ["tiles"]); + const field = new Field(init); + field.tiles = tiles; return field; } - toLogJSON() { - const field = this.field.map((cell) => { - return Object.assign({}, cell); - }); - return { field, board: null }; - } set(x, y, att, playerid) { if (playerid !== null && playerid < 0) { throw Error("playerid must be 0 or more"); } - this.field[x + y * this.board.w] = { type: att, player: playerid }; + this.tiles[x + y * this.width] = { type: att, player: playerid }; } get(x, y) { - return this.field[x + y * this.board.w]; + return this.tiles[x + y * this.width]; } setAgent(playerid, x, y) { const { type: att, player: pid } = this.get(x, y); @@ -486,15 +392,15 @@ class Field { this.set(x, y, Field.WALL, playerid); return true; } - fillBase() { + fillArea() { // プレイヤーごとに入れ子関係なく囲まれている所にフラグを立て、まとめる。 // (bitごと 例:010だったら、1番目のプレイヤーの領地or城壁であるという意味) // 各マスの立っているbitが一つだけだったらそのプレイヤーの領地or城壁で確定。 // 2つ以上bitが立っていたら入れ子になっているので、その部分だけmaskをかけ、もう一度最初からやり直す。 // (whileするたびに入れ子が一個ずつ解消されていくイメージ) // 説明難しい… - const w = this.board.w; - const h = this.board.h; + const w = this.width; + const h = this.height; const field = []; // 外側に空白のマスを作る for (let y = -1; y < h + 1; y++) { @@ -503,7 +409,7 @@ class Field { field.push({ type: Field.AREA, player: null }); } else - field.push(Object.assign({}, this.field[x + y * w])); + field.push(Object.assign({}, this.tiles[x + y * w])); } } const mask = new Array(field.length); @@ -511,7 +417,7 @@ class Field { mask[i] = 1; while (mask.reduce((s, c) => s + c)) { const area = new Array(field.length); - for (let pid = 0; pid < this.board.nplayer; pid++) { + for (let pid = 0; pid < this.nPlayer; pid++) { for (let i = 0; i < field.length; i++) { area[i] |= 1 << pid; } @@ -558,22 +464,22 @@ class Field { for (let j = 0; j < h; j++) { const n = i + j * w; const nexp = (i + 1) + (j + 1) * (w + 2); - if (this.field[n].type !== Field.WALL) { - this.field[n].player = field[nexp].player; + if (this.tiles[n].type !== Field.WALL) { + this.tiles[n].player = field[nexp].player; } } } } getPoints() { const points = []; - for (let i = 0; i < this.board.nplayer; i++) { + for (let i = 0; i < this.nPlayer; i++) { points[i] = { areaPoint: 0, wallPoint: 0 }; } - this.field.forEach(({ type: att, player: pid }, idx) => { + this.tiles.forEach(({ type: att, player: pid }, idx) => { if (pid === null) return; const p = points[pid]; - const pnt = this.board.points[idx]; + const pnt = this.points[idx]; if (att === Field.WALL) { p.wallPoint += pnt; } @@ -583,9 +489,6 @@ class Field { }); return points; } - getJSON() { - return this.field; - } } exports.Field = Field; Object.defineProperty(Field, "AREA", { @@ -601,8 +504,9 @@ Object.defineProperty(Field, "WALL", { value: 1 }); class Game { - constructor(board) { - Object.defineProperty(this, "board", { + constructor(gameInit) { + _Game_instances.add(this); + Object.defineProperty(this, "totalTurn", { enumerable: true, configurable: true, writable: true, @@ -614,30 +518,6 @@ class Game { writable: true, value: void 0 }); - Object.defineProperty(this, "nturn", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nsec", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "gaming", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "ending", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); Object.defineProperty(this, "field", { enumerable: true, configurable: true, @@ -656,314 +536,242 @@ class Game { writable: true, value: void 0 }); - this.board = board; + const { totalTurn = 30 } = gameInit, fieldInit = __rest(gameInit, ["totalTurn"]); + this.totalTurn = totalTurn; this.players = []; - this.nturn = board.nturn; - this.nsec = board.nsec; - this.gaming = false; - this.ending = false; - this.field = new Field(board); + this.field = new Field(fieldInit); this.log = []; this.turn = 0; } - static restore(data) { - const board = Board.restore(data.board); + static fromJSON(data) { + const board = { + width: data.field.width, + height: data.field.height, + points: data.field.points, + nAgent: data.field.nAgent, + nPlayer: data.field.nPlayer, + totalTurn: data.totalTurn, + }; const game = new Game(board); - game.players = data.players.map((p) => Player.restore(p)); - game.gaming = data.gaming; - game.ending = data.ending; - game.field.field = data.field.field; + game.players = data.players.map((p) => Player.fromJSON(p, game)); + game.field.tiles = data.field.tiles; game.log = data.log; game.turn = data.turn; return game; } - toLogJSON() { - const data = Object.assign(Object.assign({}, this), { field: this.field.toLogJSON() }); - data.players = data.players.map((p) => p.toLogJSON()); - data.board = data.board.toLogJSON(); - return data; - } attachPlayer(player) { if (!this.isFree()) return false; if (this.players.indexOf(player) >= 0) return false; - player.index = this.players.length; - this.players.push(player); + player.index = this.players.push(player) - 1; player.setGame(this); return true; } - isReady() { - return this.players.length === this.board.nplayer; + // status : free -> ready -> gaming -> ended + getStatus() { + if (this.turn === 0) { + if (this.players.length < this.field.nPlayer) + return "free"; + else + return "ready"; + } + else if (this.log.length !== this.totalTurn) { + return "gaming"; + } + else { + return "ended"; + } } isFree() { - return !this.isReady() && !this.gaming && !this.ending; + return this.getStatus() === "free"; + } + isReady() { + return this.getStatus() === "ready"; } isGaming() { - return this.gaming && !this.ending; + return this.getStatus() === "gaming"; + } + isEnded() { + return this.getStatus() === "ended"; } start() { this.turn = 1; - this.gaming = true; - this.players.forEach((p) => p.noticeStart()); } - /*setActions(player, actions) { - this.actions[player] = actions; - }*/ nextTurn() { const actions = []; this.players.forEach((p, idx) => actions[idx] = p.getActions()); // console.log("actions", actions); - this.checkActions(actions); // 同じエージェントの2回移動、画面外など無効な操作をチェック - this.revertNotOwnerWall(); // PUT, MOVE先が敵陣壁ではないか?チェックし無効化 - this.checkConflict(actions); // 同じマスを差しているものはすべて無効 // 壁remove & move は、removeが有効 - this.revertOverlap(); // 仮に配置または動かし、かぶったところをrevert - this.putOrMove(); // 配置または動かし、フィールド更新 - this.removeOrNot(); // AgentがいるところをREMOVEしているものはrevert - this.commit(); - this.checkAgentConflict(); - this.field.fillBase(); + __classPrivateFieldGet(this, _Game_instances, "m", _Game_checkActions).call(this, actions); // 同じエージェントの2回移動、画面外など無効な操作をチェック + __classPrivateFieldGet(this, _Game_instances, "m", _Game_revertNotOwnerWall).call(this); // PUT, MOVE先が敵陣壁ではないか?チェックし無効化 + __classPrivateFieldGet(this, _Game_instances, "m", _Game_checkConflict).call(this, actions); // 同じマスを差しているものはすべて無効 // 壁remove & move は、removeが有効 + __classPrivateFieldGet(this, _Game_instances, "m", _Game_revertOverlap).call(this); // 仮に配置または動かし、かぶったところをrevert + __classPrivateFieldGet(this, _Game_instances, "m", _Game_putOrMove).call(this); // 配置または動かし、フィールド更新 + __classPrivateFieldGet(this, _Game_instances, "m", _Game_removeOrNot).call(this); // AgentがいるところをREMOVEしているものはrevert + __classPrivateFieldGet(this, _Game_instances, "m", _Game_commit).call(this); + this.field.fillArea(); this.log.push({ players: actions.map((ar, idx) => { return { point: this.field.getPoints()[idx], - actions: ar.map((a) => a.getJSON()), + actions: ar, }; }), }); - if (this.turn < this.nturn) { + this.players.forEach((p) => p.clearActions()); + if (this.turn < this.totalTurn) { this.turn++; + return true; } else { - this.gaming = false; - this.ending = true; - } - this.players.forEach((p) => p.clearActions()); - return this.gaming; - } - checkActions(actions) { - const nplayer = actions.length; - // 範囲外と、かぶりチェック - for (let playerid = 0; playerid < nplayer; playerid++) { - const done = {}; - actions[playerid].forEach((a) => { - const aid = a.agentid; - const agents = this.players[playerid].agents; - if (aid < 0 || aid >= agents.length) { - a.res = Action.ERR_ILLEGAL_AGENT; - return; - } - const doneAgent = done[aid]; - if (doneAgent) { - a.res = Action.ERR_ONLY_ONE_TURN; - doneAgent.res = Action.ERR_ONLY_ONE_TURN; - return; - } - done[aid] = a; - }); - } - // 変な動きチェック - for (let playerid = 0; playerid < nplayer; playerid++) { - actions[playerid].filter((a) => a.res === Action.SUCCESS).forEach((a) => { - const aid = a.agentid; - const agents = this.players[playerid].agents; - const agent = agents[aid]; - if (!agent.check(a)) { - a.res = Action.ERR_ILLEGAL_ACTION; - return; - } - }); - } - } - checkConflict(actions) { - //console.log("Actions", actions); - const chkfield = new Array(this.field.field.length); - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; - } - const nplayer = actions.length; - for (let playerid = 0; playerid < nplayer; playerid++) { - actions[playerid].forEach((a) => { - if (a.res !== Action.SUCCESS) - return false; - const n = a.x + a.y * this.board.w; - if (n >= 0 && n < chkfield.length) { - chkfield[n].push(a); - } - else { - console.log("?? n", n); - } - }); - } - // PUT/MOVE/REMOVE、競合はすべて無効 - chkfield.filter((a) => a.length >= 2).forEach((a) => { - // console.log("conflict", a); - a.forEach((action) => action.res = Action.CONFLICT); - }); - } - checkAgentConflict() { - const chkfield = new Array(this.field.field.length); - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; + return false; } - (0, util_js_1.flat)(this.players.map((p) => p.agents)).forEach((agent) => { - if (agent.x === -1) - return; - const _act = agent.lastaction; - const n = agent.x + agent.y * this.board.w; - chkfield[n].push(agent); - // console.log("agent", agent.playerid, agent.x, agent.y); - }); - chkfield.filter((a) => a.length >= 2).forEach((a) => { - console.log("**\nduplicate!!", a); - throw Error("**\nduplicate!!"); - //Deno.exit(0); - }); } - putOrMove() { - (0, util_js_1.flat)(this.players.map((p) => p.agents)).forEach((agent) => { - if (!agent.isValidAction()) - return; - if (!agent.putOrMove()) { - // throw new Error("illegal action!") - // console.log(`throw new Error("illegal action!")`); +} +exports.Game = Game; +_Game_instances = new WeakSet(), _Game_checkActions = function _Game_checkActions(actions) { + const nplayer = actions.length; + // 範囲外と、かぶりチェック + for (let playerid = 0; playerid < nplayer; playerid++) { + const done = {}; + actions[playerid].forEach((a) => { + const aid = a.agentId; + const agents = this.players[playerid].agents; + if (aid < 0 || aid >= agents.length) { + a.res = Action.ERR_ILLEGAL_AGENT; return; } - }); - } - revertOverlap() { - let reverts = false; - const chkfield = new Array(this.field.field.length); - do { - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; - } - (0, util_js_1.flat)(this.players.map((p) => p.agents)).forEach((agent) => { - const act = agent.lastaction; - if (agent.isValidAction() && act && - (act.type === Action.MOVE || act.type === Action.PUT)) { - const n = act.x + act.y * this.board.w; - //console.log("act", n); - chkfield[n].push(agent); - } - else { - if (agent.x === -1) - return; - const n = agent.x + agent.y * this.board.w; - //console.log("agent", n); - chkfield[n].push(agent); - } - }); - reverts = false; - //console.log("chkfield", chkfield); - chkfield.filter((a) => a.length >= 2).forEach((a) => { - // console.log("**\nreverts", a); - a.forEach((agent) => agent.revert()); - reverts = true; - }); - //console.log(reverts); - } while (reverts); // revertがあったら再度全件チェック - } - removeOrNot() { - const agents = (0, util_js_1.flat)(this.players.map((p) => p.agents)); - agents.forEach((agent) => { - if (agent.x === -1) - return; - if (!agent.isValidAction()) + const doneAgent = done[aid]; + if (doneAgent) { + a.res = Action.ERR_ONLY_ONE_TURN; + doneAgent.res = Action.ERR_ONLY_ONE_TURN; return; - const act = agent.lastaction; - if (!act) - return; - if (act.type !== Action.REMOVE) - return; - if (agents.find((a) => a.x === act.x && a.y === act.y)) { - act.res = Action.REVERT; - } - else { - agent.remove(); } + done[aid] = a; }); } - revertNotOwnerWall() { - const agents = (0, util_js_1.flat)(this.players.map((p) => p.agents)); - const fld = this.field.field; - const w = this.board.w; - agents.forEach((agent) => { - if (agent.x === -1) - return; - if (!agent.isValidAction()) + // 変な動きチェック + for (let playerid = 0; playerid < nplayer; playerid++) { + actions[playerid].filter((a) => a.res === Action.SUCCESS).forEach((a) => { + const aid = a.agentId; + const agents = this.players[playerid].agents; + const agent = agents[aid]; + if (!agent.check(a)) { + a.res = Action.ERR_ILLEGAL_ACTION; return; - const act = agent.lastaction; - if (!act) - return; - if (act.type !== Action.MOVE && act.type !== Action.PUT) - return; - // only PUT & MOVE - const n = act.x + act.y * w; - const f = fld[n]; - const iswall = f.type === Field.WALL; - const owner = f.player; - if (iswall && owner !== agent.playerid && owner !== -1) { - agent.revert(); } }); } - commit() { - const agents = (0, util_js_1.flat)(this.players.map((p) => p.agents)); - agents.forEach((agent) => { - // if (agent.x === -1) return; - // if (!agent.isValidAction()) return; - agent.commit(); +}, _Game_checkConflict = function _Game_checkConflict(actions) { + //console.log("Actions", actions); + const chkfield = new Array(this.field.tiles.length); + for (let i = 0; i < chkfield.length; i++) { + chkfield[i] = []; + } + const nplayer = actions.length; + for (let playerid = 0; playerid < nplayer; playerid++) { + actions[playerid].forEach((a) => { + if (a.res !== Action.SUCCESS) + return false; + const n = a.x + a.y * this.field.width; + if (n >= 0 && n < chkfield.length) { + chkfield[n].push(a); + } }); } - getStatusJSON() { - return { - players: this.players.map((p) => p.getJSON()), - board: this.board.getJSON(), - field: this.field.getJSON(), - agents: this.players.map((p) => p.agents.map((a) => a.getJSON())), - points: this.field.getPoints(), - log: this.log, - }; - } - toJSON() { - const players = []; - this.players.forEach((p, i) => { - const id = p.id; - let agents = []; - if (this.isReady()) { - agents = []; - p.agents.forEach((a) => { - const agent = { - x: a.x, - y: a.y, - }; - agents.push(agent); - }); + // PUT/MOVE/REMOVE、競合はすべて無効 + chkfield.filter((a) => a.length >= 2).forEach((a) => { + // console.log("conflict", a); + a.forEach((action) => action.res = Action.CONFLICT); + }); +}, _Game_putOrMove = function _Game_putOrMove() { + (0, util_js_1.flat)(this.players.map((p) => p.agents)).forEach((agent) => { + if (!agent.isValidAction()) + return; + if (!agent.putOrMove()) { + // throw new Error("illegal action!") + // console.log(`throw new Error("illegal action!")`); + return; + } + }); +}, _Game_revertOverlap = function _Game_revertOverlap() { + let reverts = false; + const chkfield = new Array(this.field.tiles.length); + do { + for (let i = 0; i < chkfield.length; i++) { + chkfield[i] = []; + } + (0, util_js_1.flat)(this.players.map((p) => p.agents)).forEach((agent) => { + const act = agent.isValidAction(); + if (act && + (act.type === Action.MOVE || act.type === Action.PUT)) { + const n = act.x + act.y * this.field.width; + //console.log("act", n); + chkfield[n].push(agent); + } + else { + if (agent.x === -1) + return; + const n = agent.x + agent.y * this.field.width; + //console.log("agent", n); + chkfield[n].push(agent); } - const player = { - id: id, - agents: agents, - point: this.field.getPoints()[i], - }; - players.push(player); }); - let board = null; - if (this.isReady()) - board = this.board; - return { - gaming: this.gaming, - ending: this.ending, - board: board ? board.toJSON() : null, - turn: this.turn, - totalTurn: this.nturn, - tiled: this.isReady() ? this.field.field : null, - players: players, - log: this.log, - }; - } -} -exports.Game = Game; + reverts = false; + //console.log("chkfield", chkfield); + chkfield.filter((a) => a.length >= 2).forEach((a) => { + // console.log("**\nreverts", a); + a.forEach((agent) => agent.revert()); + reverts = true; + }); + //console.log(reverts); + } while (reverts); // revertがあったら再度全件チェック +}, _Game_removeOrNot = function _Game_removeOrNot() { + const agents = (0, util_js_1.flat)(this.players.map((p) => p.agents)); + agents.forEach((agent) => { + if (agent.x === -1) + return; + const act = agent.isValidAction(); + if (!act) + return; + if (act.type !== Action.REMOVE) + return; + if (agents.find((a) => a.x === act.x && a.y === act.y)) { + act.res = Action.REVERT; + } + else { + agent.remove(); + } + }); +}, _Game_revertNotOwnerWall = function _Game_revertNotOwnerWall() { + const agents = (0, util_js_1.flat)(this.players.map((p) => p.agents)); + const fld = this.field.tiles; + const w = this.field.width; + agents.forEach((agent) => { + if (agent.x === -1) + return; + const act = agent.isValidAction(); + if (!act) + return; + if (act.type !== Action.MOVE && act.type !== Action.PUT) + return; + // only PUT & MOVE + const n = act.x + act.y * w; + const f = fld[n]; + const iswall = f.type === Field.WALL; + const owner = f.player; + if (iswall && owner !== agent.playerIdx && owner !== -1) { + agent.revert(); + } + }); +}, _Game_commit = function _Game_commit() { + const agents = (0, util_js_1.flat)(this.players.map((p) => p.agents)); + agents.forEach((agent) => { + // if (agent.x === -1) return; + // if (!agent.isValidAction()) return; + agent.commit(); + }); +}; class Player { constructor(id, spec = "") { Object.defineProperty(this, "id", { @@ -1009,31 +817,32 @@ class Player { this.index = -1; this.agents = []; } - static restore(data, game) { + static fromJSON(data, game) { const player = new Player(data.id, data.spec); player.index = data.index; if (game) { player.game = game; player.agents = data.agents.map((a) => { - return Agent.restore(a, game.board, game.field); + return Agent.fromJSON(a, player.index, game.field); }); } return player; } - toLogJSON() { - const p = Object.assign({}, this); - p.game = null; - p.agents = p.agents.map((a) => a.toLogJSON()); - return p; + toJSON() { + return { + id: this.id, + spec: this.spec, + index: this.index, + actions: this.actions, + agents: this.agents, + }; } setGame(game) { this.game = game; - for (let j = 0; j < game.board.nagent; j++) { - this.agents.push(new Agent(game.board, game.field, this.index)); + for (let j = 0; j < game.field.nAgent; j++) { + this.agents.push(new Agent(game.field, this.index)); } } - noticeStart() { - } setActions(actions) { if (this.game === null) throw new Error("game is null"); @@ -1046,65 +855,5 @@ class Player { clearActions() { this.actions = []; } - getJSON() { - return { - userId: this.id, - spec: this.spec, - index: this.index, - }; - } } exports.Player = Player; -class Kakomimasu { - constructor() { - Object.defineProperty(this, "games", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "boards", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - this.games = []; - this.boards = []; - } - appendBoard(board) { - this.boards.push(board); - } - getBoards() { - return this.boards; - } - /** - * @deprecated use addGame - */ - createGame(...param) { - //console.log(board); - const game = new Game(...param); - this.games.push(game); - return game; - } - addGame(game) { - this.games.push(game); - return game; - } - getGames() { - return this.games; - } - getFreeGames() { - return this.games.filter((g) => g.isFree()); - } - /** - * @deprecated use Player class - */ - createPlayer(playername, spec = "") { - if (spec == null) - return new Player(playername); - else - return new Player(playername, spec); - } -} -exports.Kakomimasu = Kakomimasu; diff --git a/cjs/json_type.js b/cjs/json_type.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/cjs/json_type.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/deno.json b/deno.json index a7ff7cf..4fe9763 100644 --- a/deno.json +++ b/deno.json @@ -10,6 +10,10 @@ } }, "tasks": { - "dnt": "deno run -A scripts/dnt.ts" + "dnt": "deno run -A scripts/dnt.ts", + "test": "rm -rf coverage && deno test --allow-read --coverage=coverage --parallel", + "cov": "deno coverage ./coverage --lcov --output=coverage/coverage.lcov", + "cov:report": "genhtml -o coverage-html coverage/coverage.lcov", + "test-report": "deno task test ; deno task cov && deno task cov:report" } } diff --git a/deno.lock b/deno.lock index 0ecabf0..ef8e144 100644 --- a/deno.lock +++ b/deno.lock @@ -1,6 +1,7 @@ { "version": "2", "remote": { + "https://code4sabae.github.io/js/MersenneTwister.js": "a47fdcce5a6d1849cb0840efebd1846304eade3de1b556a18f6611aba78d81fd", "https://deno.land/std@0.106.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58", "https://deno.land/std@0.106.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac", "https://deno.land/std@0.106.0/fs/_util.ts": "f2ce811350236ea8c28450ed822a5f42a0892316515b1cd61321dec13569c56b", @@ -27,6 +28,9 @@ "https://deno.land/std@0.109.0/path/posix.ts": "34349174b9cd121625a2810837a82dd8b986bbaaad5ade690d1de75bbb4555b2", "https://deno.land/std@0.109.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", "https://deno.land/std@0.109.0/path/win32.ts": "2edb2f71f10578ee1168de01a8cbd3c65483e45a46bc2fa3156a0c6bfbd2720d", + "https://deno.land/std@0.111.0/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621", + "https://deno.land/std@0.111.0/testing/_diff.ts": "ccd6c3af6e44c74bf1591acb1361995f5f50df64323a6e7fb3f16c8ea792c940", + "https://deno.land/std@0.111.0/testing/asserts.ts": "6b0d6ba564bdff807bd0f0e93e02c48aa3177acf19416bf84a7f420191ef74cd", "https://deno.land/x/dnt@0.0.9/lib/compiler.ts": "32ec582f5c20c82372d53550381eb9d44b6a8d9dbabffe7b4807557ebfbdd616", "https://deno.land/x/dnt@0.0.9/lib/mod.deps.ts": "82e53a28f5768b72244f46bcc821a8405c9103c7c00c34c94563595d0ffd1f4b", "https://deno.land/x/dnt@0.0.9/lib/pkg/dnt_wasm.js": "52305bf88fc54e8a251a92b7f1a6b695803d4f83b193f61d308438f7effce596", diff --git a/mjs/Kakomimasu.js b/mjs/Kakomimasu.js index 4d76473..8eabff4 100644 --- a/mjs/Kakomimasu.js +++ b/mjs/Kakomimasu.js @@ -1,115 +1,37 @@ -import { flat } from "./util.js"; -class Board { - constructor({ w, h, points, nagent, nturn, nsec, nplayer, name }) { - Object.defineProperty(this, "w", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "h", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "points", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nagent", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nturn", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nsec", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nplayer", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "name", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - this.w = w; - this.h = h; - this.points = points; - this.nagent = nagent; - this.nturn = nturn || 30; - this.nsec = nsec || 3; - this.nplayer = nplayer || 2; - this.name = name || ""; - if (this.points.length !== this.w * this.h) { - throw new Error("points.length must be " + this.w * this.h); +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; } - //console.log("board", this, this.name); - // if (!(w >= 12 && w <= 24 && h >= 12 && h <= 24)) { throw new Error("w and h 12-24"); } - } - static restore(data) { - const board = new Board(data); - return board; - } - toLogJSON() { - return Object.assign({}, this); - } - getJSON() { - return { - name: this.name, - w: this.w, - h: this.h, - points: this.points, - nagents: this.nagent, - nturn: this.nturn, - nsec: this.nsec, - nplayer: this.nplayer, - }; - } - toJSON() { - return { - name: this.name, - width: this.w, - height: this.h, - nAgent: this.nagent, - nPlayer: this.nplayer, - nTurn: this.nturn, - nSec: this.nsec, - points: this.points, - }; - } -} + return t; +}; +var _Agent_instances, _Agent_lastaction, _Agent_isOnBoard, _Agent_checkOnBoard, _Agent_checkDir, _Agent_checkPut, _Agent_checkMove, _Agent_checkRemove, _Agent_put, _Agent_move, _Game_instances, _Game_checkActions, _Game_checkConflict, _Game_putOrMove, _Game_revertOverlap, _Game_removeOrNot, _Game_revertNotOwnerWall, _Game_commit; +import { flat } from "./util.js"; class Agent { - constructor(board, field, playerid) { - Object.defineProperty(this, "board", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); + constructor(field, playeridx) { + _Agent_instances.add(this); Object.defineProperty(this, "field", { enumerable: true, configurable: true, writable: true, value: void 0 }); - Object.defineProperty(this, "playerid", { + Object.defineProperty(this, "playerIdx", { enumerable: true, configurable: true, writable: true, @@ -139,171 +61,140 @@ class Agent { writable: true, value: void 0 }); - Object.defineProperty(this, "lastaction", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - this.board = board; + _Agent_lastaction.set(this, void 0); this.field = field; - this.playerid = playerid; + this.playerIdx = playeridx; this.x = -1; this.y = -1; this.bkx = -1; this.bky = -1; - this.lastaction = null; + __classPrivateFieldSet(this, _Agent_lastaction, null, "f"); } - static restore(data, board, field) { - var _a; - const agent = new Agent(board, field, data.playerid); + static fromJSON(data, playerIdx, field) { + const agent = new Agent(field, playerIdx); agent.x = data.x; agent.y = data.y; - agent.bkx = data.bkx; - agent.bky = data.bky; - agent.lastaction = (_a = data.lastaction) !== null && _a !== void 0 ? _a : null; return agent; } - toLogJSON() { - return Object.assign(Object.assign({}, this), { board: null, field: null }); - } - isOnBoard() { - return this.x !== -1; - } - checkOnBoard(x, y) { - return x >= 0 && x < this.board.w && y >= 0 && y < this.board.h; - } - checkDir(x, y) { - if (this.x === x && this.y === y) - return false; - return Math.abs(this.x - x) <= 1 && Math.abs(this.y - y) <= 1; + toJSON() { + return { x: this.x, y: this.y }; } check(act) { - this.lastaction = act; + __classPrivateFieldSet(this, _Agent_lastaction, act, "f"); + this.bkx = this.x; + this.bky = this.y; const x = act.x; const y = act.y; const t = act.type; if (t === Action.PUT) - return this.checkPut(x, y); - if (t === Action.NONE) - return this.checkNone(x, y); - if (t === Action.MOVE) - return this.checkMove(x, y); - if (t === Action.REMOVE) - return this.checkRemove(x, y); - return false; - } - checkPut(x, y) { - if (this.isOnBoard()) - return false; - if (!this.checkOnBoard(x, y)) - return false; - return true; - } - checkNone(_x, _y) { - if (!this.isOnBoard()) - return false; - return true; - } - checkMove(x, y) { - if (!this.isOnBoard()) - return false; - if (!this.checkOnBoard(x, y)) - return false; - if (!this.checkDir(x, y)) - return false; - return true; - } - checkRemove(x, y) { - if (!this.isOnBoard()) - return false; - if (!this.checkOnBoard(x, y)) - return false; - if (!this.checkDir(x, y)) - return false; - //const _n = x + y * this.board.w; - if (this.field.get(x, y).type !== Field.WALL) + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkPut).call(this, x, y); + else if (t === Action.MOVE) + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkMove).call(this, x, y); + else if (t === Action.REMOVE) + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkRemove).call(this, x, y); + else return false; - return true; } isValidAction() { - if (!this.lastaction) - return false; - if (this.lastaction.res !== Action.SUCCESS) - return false; - return true; + if (!__classPrivateFieldGet(this, _Agent_lastaction, "f")) + return; + if (__classPrivateFieldGet(this, _Agent_lastaction, "f").res !== Action.SUCCESS) + return; + return __classPrivateFieldGet(this, _Agent_lastaction, "f"); } putOrMove() { //console.log("putormove", this); - if (this.lastaction == null) + if (__classPrivateFieldGet(this, _Agent_lastaction, "f") == null) throw new Error("putOrMove before check"); - if (this.lastaction.res !== Action.SUCCESS) + if (__classPrivateFieldGet(this, _Agent_lastaction, "f").res !== Action.SUCCESS) return false; - const act = this.lastaction; + const act = __classPrivateFieldGet(this, _Agent_lastaction, "f"); const x = act.x; const y = act.y; const t = act.type; if (t === Action.PUT) - return this.put(x, y); + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_put).call(this, x, y); if (t === Action.MOVE) - return this.move(x, y); - return true; - } - put(x, y) { - if (!this.checkPut(x, y)) - return false; - if (!this.field.setAgent(this.playerid, x, y)) { - return false; // throw new Error("can't enter the wall"); - } - this.x = x; - this.y = y; - return true; - } - none(x, y) { - if (!this.checkNone(x, y)) - return false; - return true; - } - move(x, y) { - if (!this.checkMove(x, y)) - return false; - if (!this.field.setAgent(this.playerid, x, y)) { - return false; // throw new Error("can't enter the wall"); - } - this.x = x; - this.y = y; + return __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_move).call(this, x, y); return true; } remove() { - if (this.lastaction == null) + if (__classPrivateFieldGet(this, _Agent_lastaction, "f") == null) throw new Error("remove before check"); - const { x, y } = this.lastaction; - if (!this.checkRemove(x, y)) + const { x, y } = __classPrivateFieldGet(this, _Agent_lastaction, "f"); + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkRemove).call(this, x, y)) return false; this.field.set(x, y, Field.AREA, null); return true; } commit() { - this.bkx = this.x; - this.bky = this.y; - this.lastaction = null; + __classPrivateFieldSet(this, _Agent_lastaction, null, "f"); } revert() { - this.x = this.bkx; - this.y = this.bky; - const act = this.lastaction; + const act = __classPrivateFieldGet(this, _Agent_lastaction, "f"); if (act && (act.type === Action.MOVE || act.type === Action.PUT) && act.res === Action.SUCCESS) { act.res = Action.REVERT; + this.x = this.bkx; + this.y = this.bky; } } - getJSON() { - return { x: this.x, y: this.y }; - } } +_Agent_lastaction = new WeakMap(), _Agent_instances = new WeakSet(), _Agent_isOnBoard = function _Agent_isOnBoard() { + return this.x !== -1; +}, _Agent_checkOnBoard = function _Agent_checkOnBoard(x, y) { + return x >= 0 && x < this.field.width && y >= 0 && + y < this.field.height; +}, _Agent_checkDir = function _Agent_checkDir(x, y) { + if (this.x === x && this.y === y) + return false; + return Math.abs(this.x - x) <= 1 && Math.abs(this.y - y) <= 1; +}, _Agent_checkPut = function _Agent_checkPut(x, y) { + if (__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_isOnBoard).call(this)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkOnBoard).call(this, x, y)) + return false; + return true; +}, _Agent_checkMove = function _Agent_checkMove(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_isOnBoard).call(this)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkOnBoard).call(this, x, y)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkDir).call(this, x, y)) + return false; + return true; +}, _Agent_checkRemove = function _Agent_checkRemove(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_isOnBoard).call(this)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkOnBoard).call(this, x, y)) + return false; + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkDir).call(this, x, y)) + return false; + if (this.field.get(x, y).type !== Field.WALL) + return false; + return true; +}, _Agent_put = function _Agent_put(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkPut).call(this, x, y)) + return false; + if (!this.field.setAgent(this.playerIdx, x, y)) { + return false; // throw new Error("can't enter the wall"); + } + this.x = x; + this.y = y; + return true; +}, _Agent_move = function _Agent_move(x, y) { + if (!__classPrivateFieldGet(this, _Agent_instances, "m", _Agent_checkMove).call(this, x, y)) + return false; + if (!this.field.setAgent(this.playerIdx, x, y)) { + return false; // throw new Error("can't enter the wall"); + } + this.x = x; + this.y = y; + return true; +}; class Action { constructor(agentid, type, x, y) { - Object.defineProperty(this, "agentid", { + Object.defineProperty(this, "agentId", { enumerable: true, configurable: true, writable: true, @@ -333,26 +224,17 @@ class Action { writable: true, value: void 0 }); - this.agentid = agentid; + this.agentId = agentid; this.type = type; this.x = x; this.y = y; this.res = Action.SUCCESS; } - static restore(data) { - const action = new Action(data.agentid, data.type, data.x, data.y); + static fromJSON(data) { + const action = new Action(data.agentId, data.type, data.x, data.y); action.res = data.res; return action; } - getJSON() { - return { - agentId: this.agentid, - type: this.type, - x: this.x, - y: this.y, - res: this.res, - }; - } static getMessage(res) { return [ "success", @@ -426,52 +308,77 @@ Object.defineProperty(Action, "ERR_ILLEGAL_ACTION", { writable: true, value: 5 }); -Object.defineProperty(Action, "fromJSON", { +Object.defineProperty(Action, "fromArray", { enumerable: true, configurable: true, writable: true, value: (array) => array.map((a) => new Action(a[0], a[1], a[2], a[3])) }); class Field { - constructor(board) { - Object.defineProperty(this, "board", { + constructor({ width, height, points, nAgent = 4, nPlayer = 2 }) { + Object.defineProperty(this, "width", { enumerable: true, configurable: true, writable: true, value: void 0 }); - Object.defineProperty(this, "field", { + Object.defineProperty(this, "height", { enumerable: true, configurable: true, writable: true, value: void 0 }); - this.board = board; - // field - this.field = []; - for (let i = 0; i < this.board.w * this.board.h; i++) { - this.field.push({ type: Field.AREA, player: null }); + Object.defineProperty(this, "nAgent", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "nPlayer", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "points", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "tiles", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + if (points.length !== width * height) { + throw Error("points.length must be " + width * height); } + this.width = width; + this.height = height; + this.nAgent = nAgent; + this.nPlayer = nPlayer; + this.points = points; + this.tiles = new Array(width * height).fill({ + type: Field.AREA, + player: null, + }); } - static restore(data, board) { - const field = new Field(board); - field.field = data.field; + static fromJSON(data) { + const { tiles } = data, init = __rest(data, ["tiles"]); + const field = new Field(init); + field.tiles = tiles; return field; } - toLogJSON() { - const field = this.field.map((cell) => { - return Object.assign({}, cell); - }); - return { field, board: null }; - } set(x, y, att, playerid) { if (playerid !== null && playerid < 0) { throw Error("playerid must be 0 or more"); } - this.field[x + y * this.board.w] = { type: att, player: playerid }; + this.tiles[x + y * this.width] = { type: att, player: playerid }; } get(x, y) { - return this.field[x + y * this.board.w]; + return this.tiles[x + y * this.width]; } setAgent(playerid, x, y) { const { type: att, player: pid } = this.get(x, y); @@ -480,15 +387,15 @@ class Field { this.set(x, y, Field.WALL, playerid); return true; } - fillBase() { + fillArea() { // プレイヤーごとに入れ子関係なく囲まれている所にフラグを立て、まとめる。 // (bitごと 例:010だったら、1番目のプレイヤーの領地or城壁であるという意味) // 各マスの立っているbitが一つだけだったらそのプレイヤーの領地or城壁で確定。 // 2つ以上bitが立っていたら入れ子になっているので、その部分だけmaskをかけ、もう一度最初からやり直す。 // (whileするたびに入れ子が一個ずつ解消されていくイメージ) // 説明難しい… - const w = this.board.w; - const h = this.board.h; + const w = this.width; + const h = this.height; const field = []; // 外側に空白のマスを作る for (let y = -1; y < h + 1; y++) { @@ -497,7 +404,7 @@ class Field { field.push({ type: Field.AREA, player: null }); } else - field.push(Object.assign({}, this.field[x + y * w])); + field.push(Object.assign({}, this.tiles[x + y * w])); } } const mask = new Array(field.length); @@ -505,7 +412,7 @@ class Field { mask[i] = 1; while (mask.reduce((s, c) => s + c)) { const area = new Array(field.length); - for (let pid = 0; pid < this.board.nplayer; pid++) { + for (let pid = 0; pid < this.nPlayer; pid++) { for (let i = 0; i < field.length; i++) { area[i] |= 1 << pid; } @@ -552,22 +459,22 @@ class Field { for (let j = 0; j < h; j++) { const n = i + j * w; const nexp = (i + 1) + (j + 1) * (w + 2); - if (this.field[n].type !== Field.WALL) { - this.field[n].player = field[nexp].player; + if (this.tiles[n].type !== Field.WALL) { + this.tiles[n].player = field[nexp].player; } } } } getPoints() { const points = []; - for (let i = 0; i < this.board.nplayer; i++) { + for (let i = 0; i < this.nPlayer; i++) { points[i] = { areaPoint: 0, wallPoint: 0 }; } - this.field.forEach(({ type: att, player: pid }, idx) => { + this.tiles.forEach(({ type: att, player: pid }, idx) => { if (pid === null) return; const p = points[pid]; - const pnt = this.board.points[idx]; + const pnt = this.points[idx]; if (att === Field.WALL) { p.wallPoint += pnt; } @@ -577,9 +484,6 @@ class Field { }); return points; } - getJSON() { - return this.field; - } } Object.defineProperty(Field, "AREA", { enumerable: true, @@ -594,8 +498,9 @@ Object.defineProperty(Field, "WALL", { value: 1 }); class Game { - constructor(board) { - Object.defineProperty(this, "board", { + constructor(gameInit) { + _Game_instances.add(this); + Object.defineProperty(this, "totalTurn", { enumerable: true, configurable: true, writable: true, @@ -607,30 +512,6 @@ class Game { writable: true, value: void 0 }); - Object.defineProperty(this, "nturn", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "nsec", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "gaming", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "ending", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); Object.defineProperty(this, "field", { enumerable: true, configurable: true, @@ -649,313 +530,241 @@ class Game { writable: true, value: void 0 }); - this.board = board; + const { totalTurn = 30 } = gameInit, fieldInit = __rest(gameInit, ["totalTurn"]); + this.totalTurn = totalTurn; this.players = []; - this.nturn = board.nturn; - this.nsec = board.nsec; - this.gaming = false; - this.ending = false; - this.field = new Field(board); + this.field = new Field(fieldInit); this.log = []; this.turn = 0; } - static restore(data) { - const board = Board.restore(data.board); + static fromJSON(data) { + const board = { + width: data.field.width, + height: data.field.height, + points: data.field.points, + nAgent: data.field.nAgent, + nPlayer: data.field.nPlayer, + totalTurn: data.totalTurn, + }; const game = new Game(board); - game.players = data.players.map((p) => Player.restore(p)); - game.gaming = data.gaming; - game.ending = data.ending; - game.field.field = data.field.field; + game.players = data.players.map((p) => Player.fromJSON(p, game)); + game.field.tiles = data.field.tiles; game.log = data.log; game.turn = data.turn; return game; } - toLogJSON() { - const data = Object.assign(Object.assign({}, this), { field: this.field.toLogJSON() }); - data.players = data.players.map((p) => p.toLogJSON()); - data.board = data.board.toLogJSON(); - return data; - } attachPlayer(player) { if (!this.isFree()) return false; if (this.players.indexOf(player) >= 0) return false; - player.index = this.players.length; - this.players.push(player); + player.index = this.players.push(player) - 1; player.setGame(this); return true; } - isReady() { - return this.players.length === this.board.nplayer; + // status : free -> ready -> gaming -> ended + getStatus() { + if (this.turn === 0) { + if (this.players.length < this.field.nPlayer) + return "free"; + else + return "ready"; + } + else if (this.log.length !== this.totalTurn) { + return "gaming"; + } + else { + return "ended"; + } } isFree() { - return !this.isReady() && !this.gaming && !this.ending; + return this.getStatus() === "free"; + } + isReady() { + return this.getStatus() === "ready"; } isGaming() { - return this.gaming && !this.ending; + return this.getStatus() === "gaming"; + } + isEnded() { + return this.getStatus() === "ended"; } start() { this.turn = 1; - this.gaming = true; - this.players.forEach((p) => p.noticeStart()); } - /*setActions(player, actions) { - this.actions[player] = actions; - }*/ nextTurn() { const actions = []; this.players.forEach((p, idx) => actions[idx] = p.getActions()); // console.log("actions", actions); - this.checkActions(actions); // 同じエージェントの2回移動、画面外など無効な操作をチェック - this.revertNotOwnerWall(); // PUT, MOVE先が敵陣壁ではないか?チェックし無効化 - this.checkConflict(actions); // 同じマスを差しているものはすべて無効 // 壁remove & move は、removeが有効 - this.revertOverlap(); // 仮に配置または動かし、かぶったところをrevert - this.putOrMove(); // 配置または動かし、フィールド更新 - this.removeOrNot(); // AgentがいるところをREMOVEしているものはrevert - this.commit(); - this.checkAgentConflict(); - this.field.fillBase(); + __classPrivateFieldGet(this, _Game_instances, "m", _Game_checkActions).call(this, actions); // 同じエージェントの2回移動、画面外など無効な操作をチェック + __classPrivateFieldGet(this, _Game_instances, "m", _Game_revertNotOwnerWall).call(this); // PUT, MOVE先が敵陣壁ではないか?チェックし無効化 + __classPrivateFieldGet(this, _Game_instances, "m", _Game_checkConflict).call(this, actions); // 同じマスを差しているものはすべて無効 // 壁remove & move は、removeが有効 + __classPrivateFieldGet(this, _Game_instances, "m", _Game_revertOverlap).call(this); // 仮に配置または動かし、かぶったところをrevert + __classPrivateFieldGet(this, _Game_instances, "m", _Game_putOrMove).call(this); // 配置または動かし、フィールド更新 + __classPrivateFieldGet(this, _Game_instances, "m", _Game_removeOrNot).call(this); // AgentがいるところをREMOVEしているものはrevert + __classPrivateFieldGet(this, _Game_instances, "m", _Game_commit).call(this); + this.field.fillArea(); this.log.push({ players: actions.map((ar, idx) => { return { point: this.field.getPoints()[idx], - actions: ar.map((a) => a.getJSON()), + actions: ar, }; }), }); - if (this.turn < this.nturn) { + this.players.forEach((p) => p.clearActions()); + if (this.turn < this.totalTurn) { this.turn++; + return true; } else { - this.gaming = false; - this.ending = true; - } - this.players.forEach((p) => p.clearActions()); - return this.gaming; - } - checkActions(actions) { - const nplayer = actions.length; - // 範囲外と、かぶりチェック - for (let playerid = 0; playerid < nplayer; playerid++) { - const done = {}; - actions[playerid].forEach((a) => { - const aid = a.agentid; - const agents = this.players[playerid].agents; - if (aid < 0 || aid >= agents.length) { - a.res = Action.ERR_ILLEGAL_AGENT; - return; - } - const doneAgent = done[aid]; - if (doneAgent) { - a.res = Action.ERR_ONLY_ONE_TURN; - doneAgent.res = Action.ERR_ONLY_ONE_TURN; - return; - } - done[aid] = a; - }); - } - // 変な動きチェック - for (let playerid = 0; playerid < nplayer; playerid++) { - actions[playerid].filter((a) => a.res === Action.SUCCESS).forEach((a) => { - const aid = a.agentid; - const agents = this.players[playerid].agents; - const agent = agents[aid]; - if (!agent.check(a)) { - a.res = Action.ERR_ILLEGAL_ACTION; - return; - } - }); - } - } - checkConflict(actions) { - //console.log("Actions", actions); - const chkfield = new Array(this.field.field.length); - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; - } - const nplayer = actions.length; - for (let playerid = 0; playerid < nplayer; playerid++) { - actions[playerid].forEach((a) => { - if (a.res !== Action.SUCCESS) - return false; - const n = a.x + a.y * this.board.w; - if (n >= 0 && n < chkfield.length) { - chkfield[n].push(a); - } - else { - console.log("?? n", n); - } - }); - } - // PUT/MOVE/REMOVE、競合はすべて無効 - chkfield.filter((a) => a.length >= 2).forEach((a) => { - // console.log("conflict", a); - a.forEach((action) => action.res = Action.CONFLICT); - }); - } - checkAgentConflict() { - const chkfield = new Array(this.field.field.length); - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; + return false; } - flat(this.players.map((p) => p.agents)).forEach((agent) => { - if (agent.x === -1) - return; - const _act = agent.lastaction; - const n = agent.x + agent.y * this.board.w; - chkfield[n].push(agent); - // console.log("agent", agent.playerid, agent.x, agent.y); - }); - chkfield.filter((a) => a.length >= 2).forEach((a) => { - console.log("**\nduplicate!!", a); - throw Error("**\nduplicate!!"); - //Deno.exit(0); - }); } - putOrMove() { - flat(this.players.map((p) => p.agents)).forEach((agent) => { - if (!agent.isValidAction()) - return; - if (!agent.putOrMove()) { - // throw new Error("illegal action!") - // console.log(`throw new Error("illegal action!")`); +} +_Game_instances = new WeakSet(), _Game_checkActions = function _Game_checkActions(actions) { + const nplayer = actions.length; + // 範囲外と、かぶりチェック + for (let playerid = 0; playerid < nplayer; playerid++) { + const done = {}; + actions[playerid].forEach((a) => { + const aid = a.agentId; + const agents = this.players[playerid].agents; + if (aid < 0 || aid >= agents.length) { + a.res = Action.ERR_ILLEGAL_AGENT; return; } - }); - } - revertOverlap() { - let reverts = false; - const chkfield = new Array(this.field.field.length); - do { - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; - } - flat(this.players.map((p) => p.agents)).forEach((agent) => { - const act = agent.lastaction; - if (agent.isValidAction() && act && - (act.type === Action.MOVE || act.type === Action.PUT)) { - const n = act.x + act.y * this.board.w; - //console.log("act", n); - chkfield[n].push(agent); - } - else { - if (agent.x === -1) - return; - const n = agent.x + agent.y * this.board.w; - //console.log("agent", n); - chkfield[n].push(agent); - } - }); - reverts = false; - //console.log("chkfield", chkfield); - chkfield.filter((a) => a.length >= 2).forEach((a) => { - // console.log("**\nreverts", a); - a.forEach((agent) => agent.revert()); - reverts = true; - }); - //console.log(reverts); - } while (reverts); // revertがあったら再度全件チェック - } - removeOrNot() { - const agents = flat(this.players.map((p) => p.agents)); - agents.forEach((agent) => { - if (agent.x === -1) - return; - if (!agent.isValidAction()) + const doneAgent = done[aid]; + if (doneAgent) { + a.res = Action.ERR_ONLY_ONE_TURN; + doneAgent.res = Action.ERR_ONLY_ONE_TURN; return; - const act = agent.lastaction; - if (!act) - return; - if (act.type !== Action.REMOVE) - return; - if (agents.find((a) => a.x === act.x && a.y === act.y)) { - act.res = Action.REVERT; - } - else { - agent.remove(); } + done[aid] = a; }); } - revertNotOwnerWall() { - const agents = flat(this.players.map((p) => p.agents)); - const fld = this.field.field; - const w = this.board.w; - agents.forEach((agent) => { - if (agent.x === -1) - return; - if (!agent.isValidAction()) + // 変な動きチェック + for (let playerid = 0; playerid < nplayer; playerid++) { + actions[playerid].filter((a) => a.res === Action.SUCCESS).forEach((a) => { + const aid = a.agentId; + const agents = this.players[playerid].agents; + const agent = agents[aid]; + if (!agent.check(a)) { + a.res = Action.ERR_ILLEGAL_ACTION; return; - const act = agent.lastaction; - if (!act) - return; - if (act.type !== Action.MOVE && act.type !== Action.PUT) - return; - // only PUT & MOVE - const n = act.x + act.y * w; - const f = fld[n]; - const iswall = f.type === Field.WALL; - const owner = f.player; - if (iswall && owner !== agent.playerid && owner !== -1) { - agent.revert(); } }); } - commit() { - const agents = flat(this.players.map((p) => p.agents)); - agents.forEach((agent) => { - // if (agent.x === -1) return; - // if (!agent.isValidAction()) return; - agent.commit(); +}, _Game_checkConflict = function _Game_checkConflict(actions) { + //console.log("Actions", actions); + const chkfield = new Array(this.field.tiles.length); + for (let i = 0; i < chkfield.length; i++) { + chkfield[i] = []; + } + const nplayer = actions.length; + for (let playerid = 0; playerid < nplayer; playerid++) { + actions[playerid].forEach((a) => { + if (a.res !== Action.SUCCESS) + return false; + const n = a.x + a.y * this.field.width; + if (n >= 0 && n < chkfield.length) { + chkfield[n].push(a); + } }); } - getStatusJSON() { - return { - players: this.players.map((p) => p.getJSON()), - board: this.board.getJSON(), - field: this.field.getJSON(), - agents: this.players.map((p) => p.agents.map((a) => a.getJSON())), - points: this.field.getPoints(), - log: this.log, - }; - } - toJSON() { - const players = []; - this.players.forEach((p, i) => { - const id = p.id; - let agents = []; - if (this.isReady()) { - agents = []; - p.agents.forEach((a) => { - const agent = { - x: a.x, - y: a.y, - }; - agents.push(agent); - }); + // PUT/MOVE/REMOVE、競合はすべて無効 + chkfield.filter((a) => a.length >= 2).forEach((a) => { + // console.log("conflict", a); + a.forEach((action) => action.res = Action.CONFLICT); + }); +}, _Game_putOrMove = function _Game_putOrMove() { + flat(this.players.map((p) => p.agents)).forEach((agent) => { + if (!agent.isValidAction()) + return; + if (!agent.putOrMove()) { + // throw new Error("illegal action!") + // console.log(`throw new Error("illegal action!")`); + return; + } + }); +}, _Game_revertOverlap = function _Game_revertOverlap() { + let reverts = false; + const chkfield = new Array(this.field.tiles.length); + do { + for (let i = 0; i < chkfield.length; i++) { + chkfield[i] = []; + } + flat(this.players.map((p) => p.agents)).forEach((agent) => { + const act = agent.isValidAction(); + if (act && + (act.type === Action.MOVE || act.type === Action.PUT)) { + const n = act.x + act.y * this.field.width; + //console.log("act", n); + chkfield[n].push(agent); + } + else { + if (agent.x === -1) + return; + const n = agent.x + agent.y * this.field.width; + //console.log("agent", n); + chkfield[n].push(agent); } - const player = { - id: id, - agents: agents, - point: this.field.getPoints()[i], - }; - players.push(player); }); - let board = null; - if (this.isReady()) - board = this.board; - return { - gaming: this.gaming, - ending: this.ending, - board: board ? board.toJSON() : null, - turn: this.turn, - totalTurn: this.nturn, - tiled: this.isReady() ? this.field.field : null, - players: players, - log: this.log, - }; - } -} + reverts = false; + //console.log("chkfield", chkfield); + chkfield.filter((a) => a.length >= 2).forEach((a) => { + // console.log("**\nreverts", a); + a.forEach((agent) => agent.revert()); + reverts = true; + }); + //console.log(reverts); + } while (reverts); // revertがあったら再度全件チェック +}, _Game_removeOrNot = function _Game_removeOrNot() { + const agents = flat(this.players.map((p) => p.agents)); + agents.forEach((agent) => { + if (agent.x === -1) + return; + const act = agent.isValidAction(); + if (!act) + return; + if (act.type !== Action.REMOVE) + return; + if (agents.find((a) => a.x === act.x && a.y === act.y)) { + act.res = Action.REVERT; + } + else { + agent.remove(); + } + }); +}, _Game_revertNotOwnerWall = function _Game_revertNotOwnerWall() { + const agents = flat(this.players.map((p) => p.agents)); + const fld = this.field.tiles; + const w = this.field.width; + agents.forEach((agent) => { + if (agent.x === -1) + return; + const act = agent.isValidAction(); + if (!act) + return; + if (act.type !== Action.MOVE && act.type !== Action.PUT) + return; + // only PUT & MOVE + const n = act.x + act.y * w; + const f = fld[n]; + const iswall = f.type === Field.WALL; + const owner = f.player; + if (iswall && owner !== agent.playerIdx && owner !== -1) { + agent.revert(); + } + }); +}, _Game_commit = function _Game_commit() { + const agents = flat(this.players.map((p) => p.agents)); + agents.forEach((agent) => { + // if (agent.x === -1) return; + // if (!agent.isValidAction()) return; + agent.commit(); + }); +}; class Player { constructor(id, spec = "") { Object.defineProperty(this, "id", { @@ -1001,31 +810,32 @@ class Player { this.index = -1; this.agents = []; } - static restore(data, game) { + static fromJSON(data, game) { const player = new Player(data.id, data.spec); player.index = data.index; if (game) { player.game = game; player.agents = data.agents.map((a) => { - return Agent.restore(a, game.board, game.field); + return Agent.fromJSON(a, player.index, game.field); }); } return player; } - toLogJSON() { - const p = Object.assign({}, this); - p.game = null; - p.agents = p.agents.map((a) => a.toLogJSON()); - return p; + toJSON() { + return { + id: this.id, + spec: this.spec, + index: this.index, + actions: this.actions, + agents: this.agents, + }; } setGame(game) { this.game = game; - for (let j = 0; j < game.board.nagent; j++) { - this.agents.push(new Agent(game.board, game.field, this.index)); + for (let j = 0; j < game.field.nAgent; j++) { + this.agents.push(new Agent(game.field, this.index)); } } - noticeStart() { - } setActions(actions) { if (this.game === null) throw new Error("game is null"); @@ -1038,64 +848,5 @@ class Player { clearActions() { this.actions = []; } - getJSON() { - return { - userId: this.id, - spec: this.spec, - index: this.index, - }; - } -} -class Kakomimasu { - constructor() { - Object.defineProperty(this, "games", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - Object.defineProperty(this, "boards", { - enumerable: true, - configurable: true, - writable: true, - value: void 0 - }); - this.games = []; - this.boards = []; - } - appendBoard(board) { - this.boards.push(board); - } - getBoards() { - return this.boards; - } - /** - * @deprecated use addGame - */ - createGame(...param) { - //console.log(board); - const game = new Game(...param); - this.games.push(game); - return game; - } - addGame(game) { - this.games.push(game); - return game; - } - getGames() { - return this.games; - } - getFreeGames() { - return this.games.filter((g) => g.isFree()); - } - /** - * @deprecated use Player class - */ - createPlayer(playername, spec = "") { - if (spec == null) - return new Player(playername); - else - return new Player(playername, spec); - } } -export { Action, Agent, Board, Field, Game, Kakomimasu, Player }; +export { Action, Agent, Field, Game, Player }; diff --git a/mjs/json_type.js b/mjs/json_type.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/mjs/json_type.js @@ -0,0 +1 @@ +export {}; diff --git a/mod.ts b/mod.ts index b36ac2a..a9ca081 100644 --- a/mod.ts +++ b/mod.ts @@ -1 +1,2 @@ export * from "./src/Kakomimasu.ts"; +export * from "./src/json_type.ts"; diff --git a/package-lock.json b/package-lock.json index 0f476c1..3df035b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@kakomimasu/core", - "version": "v2.0.0-beta.1", + "version": "v2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@kakomimasu/core", - "version": "v2.0.0-beta.1", + "version": "v2.0.0", "license": "MIT" } } diff --git a/package.json b/package.json index 64325ff..0786e15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kakomimasu/core", - "version": "v2.0.0-beta.1", + "version": "v2.0.0", "description": "Kakomimasu core module for js/ts(browser/deno/node)", "license": "MIT", "repository": { diff --git a/sample/main.js b/sample/main.js index 7ee2660..e3d3253 100644 --- a/sample/main.js +++ b/sample/main.js @@ -1,31 +1,27 @@ -import { Action, Board, Kakomimasu } from "../mod.ts"; +import { Action, Game, Player } from "../mod.ts"; -const kkmm = new Kakomimasu(); - -const w = 8; -const h = 8; +const width = 8; +const height = 8; const points = []; -for (let i = 0; i < w * h; i++) { +for (let i = 0; i < width * height; i++) { points[i] = i; } -const nagent = 6; -const nturn = 10; -const board = new Board({ w, h, points, nagent, nturn }); -kkmm.appendBoard(board); +const nAgent = 6; +const totalTurn = 10; +const board = { width, height, points, nAgent, totalTurn }; -const game = kkmm.createGame(board); -const p1 = kkmm.createPlayer("test1"); -const p2 = kkmm.createPlayer("test2"); +const game = new Game(board); +const p1 = new Player("test1"); +const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); for (;;) { - const _st = game.getStatusJSON(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 1, 1], [0, Action.MOVE, 2, 2], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 1, 1], [1, Action.PUT, 1, 2], ])); @@ -33,4 +29,4 @@ for (;;) { break; } } -console.log(game.getStatusJSON()); +console.log(game); diff --git a/src/Kakomimasu.ts b/src/Kakomimasu.ts index 5cca059..49b48b8 100644 --- a/src/Kakomimasu.ts +++ b/src/Kakomimasu.ts @@ -1,210 +1,125 @@ import { flat } from "./util.ts"; +import type { + ActionJson, + AgentJson, + FieldJson, + GameJson, + PlayerJson, +} from "./json_type.ts"; export type Point = { areaPoint: number; wallPoint: number; }; -class Board { - public w: number; - public h: number; - public points: number[]; - public nagent: number; - public nturn: number; - public nsec: number; - public nplayer: number; - public name: string; - - constructor( - { w, h, points, nagent, nturn, nsec, nplayer, name }: { - w: number; - h: number; - points: number[]; - nagent: number; - nturn?: number; - nsec?: number; - nplayer?: number; - name?: string; - }, - ) { - this.w = w; - this.h = h; - this.points = points; - this.nagent = nagent; - this.nturn = nturn || 30; - this.nsec = nsec || 3; - this.nplayer = nplayer || 2; - this.name = name || ""; - if (this.points.length !== this.w * this.h) { - throw new Error("points.length must be " + this.w * this.h); - } - //console.log("board", this, this.name); - // if (!(w >= 12 && w <= 24 && h >= 12 && h <= 24)) { throw new Error("w and h 12-24"); } - } - - static restore(data: Board): Board { - const board = new Board(data); - return board; - } - - toLogJSON(): Board { - return { ...this }; - } - - getJSON(): { - name: string; - w: number; - h: number; - points: number[]; - nagents: number; - nturn: number; - nsec: number; - nplayer: number; - } { - return { - name: this.name, - w: this.w, - h: this.h, - points: this.points, - nagents: this.nagent, - nturn: this.nturn, - nsec: this.nsec, - nplayer: this.nplayer, - }; - } - - toJSON(): { - name: string; - width: number; - height: number; - nAgent: number; - nPlayer: number; - nTurn: number; - nSec: number; - points: number[]; - } { - return { - name: this.name, - width: this.w, - height: this.h, - nAgent: this.nagent, - nPlayer: this.nplayer, - nTurn: this.nturn, - nSec: this.nsec, - points: this.points, - }; - } +export interface Board { + width: number; + height: number; + points: number[]; + nAgent?: number; + nPlayer?: number; + totalTurn?: number; } class Agent { - public board: Board; - public field: Field; - public playerid: number; - public x: number; - public y: number; - public bkx: number; - public bky: number; - public lastaction: Action | null; - - constructor(board: Board, field: Field, playerid: number) { - this.board = board; + field: Field; + playerIdx: number; + x: number; + y: number; + bkx: number; + bky: number; + #lastaction: Action | null; + + constructor(field: Field, playeridx: number) { this.field = field; - this.playerid = playerid; + this.playerIdx = playeridx; this.x = -1; this.y = -1; this.bkx = -1; this.bky = -1; - this.lastaction = null; + this.#lastaction = null; } - static restore(data: Agent, board: Board, field: Field): Agent { - const agent = new Agent(board, field, data.playerid); + static fromJSON(data: AgentJson, playerIdx: number, field: Field): Agent { + const agent = new Agent(field, playerIdx); agent.x = data.x; agent.y = data.y; - agent.bkx = data.bkx; - agent.bky = data.bky; - agent.lastaction = data.lastaction ?? null; return agent; } - - toLogJSON(): Agent & { board: null; field: null } { - return { ...this, board: null, field: null }; + toJSON(): AgentJson { + return { x: this.x, y: this.y }; } - isOnBoard(): boolean { + #isOnBoard(): boolean { return this.x !== -1; } - checkOnBoard(x: number, y: number): boolean { - return x >= 0 && x < this.board.w && y >= 0 && y < this.board.h; + #checkOnBoard(x: number, y: number): boolean { + return x >= 0 && x < this.field.width && y >= 0 && + y < this.field.height; } - checkDir(x: number, y: number): boolean { + #checkDir(x: number, y: number): boolean { if (this.x === x && this.y === y) return false; return Math.abs(this.x - x) <= 1 && Math.abs(this.y - y) <= 1; } check(act: Action): boolean { - this.lastaction = act; + this.#lastaction = act; + this.bkx = this.x; + this.bky = this.y; const x = act.x; const y = act.y; const t = act.type; - if (t === Action.PUT) return this.checkPut(x, y); - if (t === Action.NONE) return this.checkNone(x, y); - if (t === Action.MOVE) return this.checkMove(x, y); - if (t === Action.REMOVE) return this.checkRemove(x, y); - return false; - } - - checkPut(x: number, y: number): boolean { - if (this.isOnBoard()) return false; - if (!this.checkOnBoard(x, y)) return false; - return true; + if (t === Action.PUT) return this.#checkPut(x, y); + else if (t === Action.MOVE) return this.#checkMove(x, y); + else if (t === Action.REMOVE) return this.#checkRemove(x, y); + else return false; } - checkNone(_x: number, _y: number): boolean { - if (!this.isOnBoard()) return false; + #checkPut(x: number, y: number): boolean { + if (this.#isOnBoard()) return false; + if (!this.#checkOnBoard(x, y)) return false; return true; } - checkMove(x: number, y: number) { - if (!this.isOnBoard()) return false; - if (!this.checkOnBoard(x, y)) return false; - if (!this.checkDir(x, y)) return false; + #checkMove(x: number, y: number) { + if (!this.#isOnBoard()) return false; + if (!this.#checkOnBoard(x, y)) return false; + if (!this.#checkDir(x, y)) return false; return true; } - checkRemove(x: number, y: number) { - if (!this.isOnBoard()) return false; - if (!this.checkOnBoard(x, y)) return false; - if (!this.checkDir(x, y)) return false; - //const _n = x + y * this.board.w; + #checkRemove(x: number, y: number) { + if (!this.#isOnBoard()) return false; + if (!this.#checkOnBoard(x, y)) return false; + if (!this.#checkDir(x, y)) return false; if (this.field.get(x, y).type !== Field.WALL) return false; return true; } - isValidAction(): boolean { - if (!this.lastaction) return false; - if (this.lastaction.res !== Action.SUCCESS) return false; - return true; + isValidAction(): Action | undefined { + if (!this.#lastaction) return; + if (this.#lastaction.res !== Action.SUCCESS) return; + return this.#lastaction; } putOrMove(): boolean { //console.log("putormove", this); - if (this.lastaction == null) throw new Error("putOrMove before check"); - if (this.lastaction.res !== Action.SUCCESS) return false; - const act = this.lastaction; + if (this.#lastaction == null) throw new Error("putOrMove before check"); + if (this.#lastaction.res !== Action.SUCCESS) return false; + const act = this.#lastaction; const x = act.x; const y = act.y; const t = act.type; - if (t === Action.PUT) return this.put(x, y); - if (t === Action.MOVE) return this.move(x, y); + if (t === Action.PUT) return this.#put(x, y); + if (t === Action.MOVE) return this.#move(x, y); return true; } - put(x: number, y: number): boolean { - if (!this.checkPut(x, y)) return false; - if (!this.field.setAgent(this.playerid, x, y)) { + #put(x: number, y: number): boolean { + if (!this.#checkPut(x, y)) return false; + if (!this.field.setAgent(this.playerIdx, x, y)) { return false; // throw new Error("can't enter the wall"); } this.x = x; @@ -212,14 +127,9 @@ class Agent { return true; } - none(x: number, y: number): boolean { - if (!this.checkNone(x, y)) return false; - return true; - } - - move(x: number, y: number): boolean { - if (!this.checkMove(x, y)) return false; - if (!this.field.setAgent(this.playerid, x, y)) { + #move(x: number, y: number): boolean { + if (!this.#checkMove(x, y)) return false; + if (!this.field.setAgent(this.playerIdx, x, y)) { return false; // throw new Error("can't enter the wall"); } this.x = x; @@ -228,91 +138,69 @@ class Agent { } remove(): boolean { - if (this.lastaction == null) throw new Error("remove before check"); - const { x, y } = this.lastaction; - if (!this.checkRemove(x, y)) return false; + if (this.#lastaction == null) throw new Error("remove before check"); + const { x, y } = this.#lastaction; + if (!this.#checkRemove(x, y)) return false; this.field.set(x, y, Field.AREA, null); return true; } commit(): void { - this.bkx = this.x; - this.bky = this.y; - this.lastaction = null; + this.#lastaction = null; } revert(): void { - this.x = this.bkx; - this.y = this.bky; - const act = this.lastaction; + const act = this.#lastaction; if ( act && (act.type === Action.MOVE || act.type === Action.PUT) && act.res === Action.SUCCESS ) { act.res = Action.REVERT; + this.x = this.bkx; + this.y = this.bky; } } - - getJSON(): { x: number; y: number } { - return { x: this.x, y: this.y }; - } } export type ActionType = 1 | 2 | 3 | 4; -type ActionRes = 0 | 1 | 2 | 3 | 4 | 5; -export type ActionJSON = [number, ActionType, number, number]; +export type ActionRes = 0 | 1 | 2 | 3 | 4 | 5; +export type ActionArray = [number, ActionType, number, number]; class Action { - public agentid: number; - public type: ActionType; - public x: number; - public y: number; - public res: ActionRes; + agentId: number; + type: ActionType; + x: number; + y: number; + res: ActionRes; // Action Type - public static readonly PUT = 1; - public static readonly NONE = 2; - public static readonly MOVE = 3; - public static readonly REMOVE = 4; + static readonly PUT = 1; + static readonly NONE = 2; + static readonly MOVE = 3; + static readonly REMOVE = 4; // Action Res - public static readonly SUCCESS = 0; - public static readonly CONFLICT = 1; - public static readonly REVERT = 2; - public static readonly ERR_ONLY_ONE_TURN = 3; - public static readonly ERR_ILLEGAL_AGENT = 4; - public static readonly ERR_ILLEGAL_ACTION = 5; + static readonly SUCCESS = 0; + static readonly CONFLICT = 1; + static readonly REVERT = 2; + static readonly ERR_ONLY_ONE_TURN = 3; + static readonly ERR_ILLEGAL_AGENT = 4; + static readonly ERR_ILLEGAL_ACTION = 5; constructor(agentid: number, type: ActionType, x: number, y: number) { - this.agentid = agentid; + this.agentId = agentid; this.type = type; this.x = x; this.y = y; this.res = Action.SUCCESS; } - static restore(data: Action) { - const action = new Action(data.agentid, data.type, data.x, data.y); + static fromJSON(data: ActionJson) { + const action = new Action(data.agentId, data.type, data.x, data.y); action.res = data.res; return action; } - getJSON(): { - agentId: number; - type: ActionType; - x: number; - y: number; - res: ActionRes; - } { - return { - agentId: this.agentid, - type: this.type, - x: this.x, - y: this.y, - res: this.res, - }; - } - static getMessage(res: ActionRes): string { return [ "success", @@ -324,51 +212,58 @@ class Action { ][res]; } - static fromJSON = (array: ActionJSON[]) => + static fromArray = (array: ActionArray[]) => array.map((a) => new Action(a[0], a[1], a[2], a[3])); } type FieldType = typeof Field.AREA | typeof Field.WALL; -type FieldCell = { type: FieldType; player: null | number }; +export type FieldTile = { type: FieldType; player: null | number }; + +export type FieldInit = Omit; class Field { - public board: Board; - public field: FieldCell[]; - - public static readonly AREA = 0; - public static readonly WALL = 1; - - constructor(board: Board) { - this.board = board; - // field - this.field = []; - for (let i = 0; i < this.board.w * this.board.h; i++) { - this.field.push({ type: Field.AREA, player: null }); + width: number; + height: number; + nAgent: number; + nPlayer: number; + points: number[]; + tiles: FieldTile[]; + + static readonly AREA = 0; + static readonly WALL = 1; + + constructor({ width, height, points, nAgent = 4, nPlayer = 2 }: FieldInit) { + if (points.length !== width * height) { + throw Error("points.length must be " + width * height); } - } - static restore(data: ReturnType, board: Board) { - const field = new Field(board); - field.field = data.field; - return field; + this.width = width; + this.height = height; + this.nAgent = nAgent; + this.nPlayer = nPlayer; + this.points = points; + this.tiles = new Array(width * height).fill({ + type: Field.AREA, + player: null, + }); } - toLogJSON() { - const field = this.field.map((cell) => { - return { ...cell }; - }); - return { field, board: null }; + static fromJSON(data: FieldJson) { + const { tiles, ...init } = data; + const field = new Field(init); + field.tiles = tiles; + return field; } set(x: number, y: number, att: FieldType, playerid: number | null): void { if (playerid !== null && playerid < 0) { throw Error("playerid must be 0 or more"); } - this.field[x + y * this.board.w] = { type: att, player: playerid }; + this.tiles[x + y * this.width] = { type: att, player: playerid }; } - get(x: number, y: number): FieldCell { - return this.field[x + y * this.board.w]; + get(x: number, y: number): FieldTile { + return this.tiles[x + y * this.width]; } setAgent(playerid: number, x: number, y: number): boolean { @@ -378,7 +273,7 @@ class Field { return true; } - fillBase(): void { + fillArea(): void { // プレイヤーごとに入れ子関係なく囲まれている所にフラグを立て、まとめる。 // (bitごと 例:010だったら、1番目のプレイヤーの領地or城壁であるという意味) // 各マスの立っているbitが一つだけだったらそのプレイヤーの領地or城壁で確定。 @@ -386,16 +281,16 @@ class Field { // (whileするたびに入れ子が一個ずつ解消されていくイメージ) // 説明難しい… - const w = this.board.w; - const h = this.board.h; - const field: FieldCell[] = []; + const w = this.width; + const h = this.height; + const field: FieldTile[] = []; // 外側に空白のマスを作る for (let y = -1; y < h + 1; y++) { for (let x = -1; x < w + 1; x++) { if (x < 0 || x >= w || y < 0 || y >= h) { field.push({ type: Field.AREA, player: null }); - } else field.push({ ...this.field[x + y * w] }); + } else field.push({ ...this.tiles[x + y * w] }); } } @@ -404,7 +299,7 @@ class Field { while (mask.reduce((s, c) => s + c)) { const area = new Array(field.length); - for (let pid = 0; pid < this.board.nplayer; pid++) { + for (let pid = 0; pid < this.nPlayer; pid++) { for (let i = 0; i < field.length; i++) { area[i] |= 1 << pid; } @@ -452,8 +347,8 @@ class Field { for (let j = 0; j < h; j++) { const n = i + j * w; const nexp = (i + 1) + (j + 1) * (w + 2); - if (this.field[n].type !== Field.WALL) { - this.field[n].player = field[nexp].player; + if (this.tiles[n].type !== Field.WALL) { + this.tiles[n].player = field[nexp].player; } } } @@ -461,13 +356,13 @@ class Field { getPoints(): Point[] { const points: ReturnType = []; - for (let i = 0; i < this.board.nplayer; i++) { + for (let i = 0; i < this.nPlayer; i++) { points[i] = { areaPoint: 0, wallPoint: 0 }; } - this.field.forEach(({ type: att, player: pid }, idx) => { + this.tiles.forEach(({ type: att, player: pid }, idx) => { if (pid === null) return; const p = points[pid]; - const pnt = this.board.points[idx]; + const pnt = this.points[idx]; if (att === Field.WALL) { p.wallPoint += pnt; } else if (att === Field.AREA) { @@ -476,138 +371,127 @@ class Field { }); return points; } - - getJSON(): FieldCell[] { - return this.field; - } } +export type GameInit = Board; + class Game { - public board: Board; - public players: Player[]; - public nturn: number; - public nsec: number; - public gaming: boolean; - public ending: boolean; - public field: Field; - public log: { + totalTurn: number; + players: Player[]; + field: Field; + log: { players: { point: Point; - actions: ReturnType[]; + actions: Action[]; }[]; }[]; - public turn: number; + turn: number; - constructor(board: Board) { - this.board = board; + constructor(gameInit: GameInit) { + const { totalTurn = 30, ...fieldInit } = gameInit; + + this.totalTurn = totalTurn; this.players = []; - this.nturn = board.nturn; - this.nsec = board.nsec; - this.gaming = false; - this.ending = false; - this.field = new Field(board); + this.field = new Field(fieldInit); this.log = []; this.turn = 0; } - static restore(data: Game): Game { - const board = Board.restore(data.board); + static fromJSON(data: GameJson): Game { + const board: GameInit = { + width: data.field.width, + height: data.field.height, + points: data.field.points, + nAgent: data.field.nAgent, + nPlayer: data.field.nPlayer, + totalTurn: data.totalTurn, + }; const game = new Game(board); - game.players = data.players.map((p) => Player.restore(p)); - game.gaming = data.gaming; - game.ending = data.ending; - game.field.field = data.field.field; + game.players = data.players.map((p) => Player.fromJSON(p, game)); + game.field.tiles = data.field.tiles; game.log = data.log; game.turn = data.turn; return game; } - toLogJSON(): Game & { field: ReturnType } { - const data: ReturnType = { - ...this, - field: this.field.toLogJSON(), - }; - data.players = data.players.map((p) => p.toLogJSON()); - data.board = data.board.toLogJSON(); - return data; - } - attachPlayer(player: Player): boolean { if (!this.isFree()) return false; if (this.players.indexOf(player) >= 0) return false; - player.index = this.players.length; - this.players.push(player); - player.setGame(this); + player.index = this.players.push(player) - 1; + player.setGame(this); return true; } - isReady(): boolean { - return this.players.length === this.board.nplayer; + // status : free -> ready -> gaming -> ended + getStatus() { + if (this.turn === 0) { + if (this.players.length < this.field.nPlayer) return "free"; + else return "ready"; + } else if (this.log.length !== this.totalTurn) { + return "gaming"; + } else { + return "ended"; + } } - - isFree(): boolean { - return !this.isReady() && !this.gaming && !this.ending; + isFree() { + return this.getStatus() === "free"; } - - isGaming(): boolean { - return this.gaming && !this.ending; + isReady() { + return this.getStatus() === "ready"; + } + isGaming() { + return this.getStatus() === "gaming"; + } + isEnded() { + return this.getStatus() === "ended"; } start(): void { this.turn = 1; - this.gaming = true; - this.players.forEach((p) => p.noticeStart()); } - /*setActions(player, actions) { - this.actions[player] = actions; - }*/ - nextTurn(): boolean { const actions: Action[][] = []; this.players.forEach((p, idx) => actions[idx] = p.getActions()); // console.log("actions", actions); - this.checkActions(actions); // 同じエージェントの2回移動、画面外など無効な操作をチェック - this.revertNotOwnerWall(); // PUT, MOVE先が敵陣壁ではないか?チェックし無効化 - this.checkConflict(actions); // 同じマスを差しているものはすべて無効 // 壁remove & move は、removeが有効 - this.revertOverlap(); // 仮に配置または動かし、かぶったところをrevert - this.putOrMove(); // 配置または動かし、フィールド更新 - this.removeOrNot(); // AgentがいるところをREMOVEしているものはrevert + this.#checkActions(actions); // 同じエージェントの2回移動、画面外など無効な操作をチェック + this.#revertNotOwnerWall(); // PUT, MOVE先が敵陣壁ではないか?チェックし無効化 + this.#checkConflict(actions); // 同じマスを差しているものはすべて無効 // 壁remove & move は、removeが有効 + this.#revertOverlap(); // 仮に配置または動かし、かぶったところをrevert + this.#putOrMove(); // 配置または動かし、フィールド更新 + this.#removeOrNot(); // AgentがいるところをREMOVEしているものはrevert - this.commit(); + this.#commit(); - this.checkAgentConflict(); - - this.field.fillBase(); + this.field.fillArea(); this.log.push({ players: actions.map((ar, idx) => { return { point: this.field.getPoints()[idx], - actions: ar.map((a) => a.getJSON()), + actions: ar, }; }), }); - if (this.turn < this.nturn) { + this.players.forEach((p) => p.clearActions()); + if (this.turn < this.totalTurn) { this.turn++; + return true; } else { - this.gaming = false; - this.ending = true; + return false; } - this.players.forEach((p) => p.clearActions()); - return this.gaming; } - checkActions(actions: Action[][]): void { + #checkActions(actions: Action[][]): void { const nplayer = actions.length; // 範囲外と、かぶりチェック for (let playerid = 0; playerid < nplayer; playerid++) { const done: Record = {}; actions[playerid].forEach((a) => { - const aid = a.agentid; + const aid = a.agentId; const agents = this.players[playerid].agents; if (aid < 0 || aid >= agents.length) { a.res = Action.ERR_ILLEGAL_AGENT; @@ -625,7 +509,7 @@ class Game { // 変な動きチェック for (let playerid = 0; playerid < nplayer; playerid++) { actions[playerid].filter((a) => a.res === Action.SUCCESS).forEach((a) => { - const aid = a.agentid; + const aid = a.agentId; const agents = this.players[playerid].agents; const agent = agents[aid]; if (!agent.check(a)) { @@ -636,9 +520,9 @@ class Game { } } - checkConflict(actions: Action[][]): void { + #checkConflict(actions: Action[][]): void { //console.log("Actions", actions); - const chkfield: Action[][] = new Array(this.field.field.length); + const chkfield: Action[][] = new Array(this.field.tiles.length); for (let i = 0; i < chkfield.length; i++) { chkfield[i] = []; } @@ -646,11 +530,9 @@ class Game { for (let playerid = 0; playerid < nplayer; playerid++) { actions[playerid].forEach((a) => { if (a.res !== Action.SUCCESS) return false; - const n = a.x + a.y * this.board.w; + const n = a.x + a.y * this.field.width; if (n >= 0 && n < chkfield.length) { chkfield[n].push(a); - } else { - console.log("?? n", n); } }); } @@ -661,27 +543,7 @@ class Game { }); } - checkAgentConflict(): void { - const chkfield = new Array(this.field.field.length); - for (let i = 0; i < chkfield.length; i++) { - chkfield[i] = []; - } - - flat(this.players.map((p) => p.agents)).forEach((agent) => { - if (agent.x === -1) return; - const _act = agent.lastaction; - const n = agent.x + agent.y * this.board.w; - chkfield[n].push(agent); - // console.log("agent", agent.playerid, agent.x, agent.y); - }); - chkfield.filter((a) => a.length >= 2).forEach((a) => { - console.log("**\nduplicate!!", a); - throw Error("**\nduplicate!!"); - //Deno.exit(0); - }); - } - - putOrMove(): void { + #putOrMove(): void { flat(this.players.map((p) => p.agents)).forEach((agent) => { if (!agent.isValidAction()) return; if (!agent.putOrMove()) { @@ -692,25 +554,25 @@ class Game { }); } - revertOverlap(): void { + #revertOverlap(): void { let reverts = false; - const chkfield: Agent[][] = new Array(this.field.field.length); + const chkfield: Agent[][] = new Array(this.field.tiles.length); do { for (let i = 0; i < chkfield.length; i++) { chkfield[i] = []; } flat(this.players.map((p) => p.agents)).forEach((agent) => { - const act = agent.lastaction; + const act = agent.isValidAction(); if ( - agent.isValidAction() && act && + act && (act.type === Action.MOVE || act.type === Action.PUT) ) { - const n = act.x + act.y * this.board.w; + const n = act.x + act.y * this.field.width; //console.log("act", n); chkfield[n].push(agent); } else { if (agent.x === -1) return; - const n = agent.x + agent.y * this.board.w; + const n = agent.x + agent.y * this.field.width; //console.log("agent", n); chkfield[n].push(agent); } @@ -726,12 +588,11 @@ class Game { } while (reverts); // revertがあったら再度全件チェック } - removeOrNot(): void { + #removeOrNot(): void { const agents = flat(this.players.map((p) => p.agents)); agents.forEach((agent) => { if (agent.x === -1) return; - if (!agent.isValidAction()) return; - const act = agent.lastaction; + const act = agent.isValidAction(); if (!act) return; if (act.type !== Action.REMOVE) return; if (agents.find((a) => a.x === act.x && a.y === act.y)) { @@ -742,14 +603,13 @@ class Game { }); } - revertNotOwnerWall(): void { + #revertNotOwnerWall(): void { const agents = flat(this.players.map((p) => p.agents)); - const fld = this.field.field; - const w = this.board.w; + const fld = this.field.tiles; + const w = this.field.width; agents.forEach((agent) => { if (agent.x === -1) return; - if (!agent.isValidAction()) return; - const act = agent.lastaction; + const act = agent.isValidAction(); if (!act) return; if (act.type !== Action.MOVE && act.type !== Action.PUT) return; // only PUT & MOVE @@ -757,13 +617,13 @@ class Game { const f = fld[n]; const iswall = f.type === Field.WALL; const owner = f.player; - if (iswall && owner !== agent.playerid && owner !== -1) { + if (iswall && owner !== agent.playerIdx && owner !== -1) { agent.revert(); } }); } - commit(): void { + #commit(): void { const agents = flat(this.players.map((p) => p.agents)); agents.forEach((agent) => { // if (agent.x === -1) return; @@ -771,84 +631,15 @@ class Game { agent.commit(); }); } - - getStatusJSON(): { - players: ReturnType[]; - board: ReturnType; - field: ReturnType; - agents: ReturnType[][]; - points: ReturnType; - log: typeof Game.prototype.log; - } { - return { - players: this.players.map((p) => p.getJSON()), - board: this.board.getJSON(), - field: this.field.getJSON(), - agents: this.players.map((p) => p.agents.map((a) => a.getJSON())), - points: this.field.getPoints(), - log: this.log, - }; - } - - toJSON(): { - gaming: typeof Game.prototype.gaming; // boolean; - ending: typeof Game.prototype.ending; //boolean; - board: ReturnType | null; - turn: typeof Game.prototype.turn; - totalTurn: typeof Game.prototype.nturn; - tiled: typeof Game.prototype.field.field | null; - players: { - id: string; - agents: { x: number; y: number }[]; - point: ReturnType[0]; - }[]; - log: typeof Game.prototype.log; - } { - const players: ReturnType["players"] = []; - this.players.forEach((p, i) => { - const id = p.id; - let agents: { x: number; y: number }[] = []; - if (this.isReady()) { - agents = []; - p.agents.forEach((a) => { - const agent = { - x: a.x, - y: a.y, - }; - agents.push(agent); - }); - } - const player = { - id: id, - agents: agents, - point: this.field.getPoints()[i], - }; - players.push(player); - }); - - let board = null; - if (this.isReady()) board = this.board; - - return { - gaming: this.gaming, - ending: this.ending, - board: board ? board.toJSON() : null, - turn: this.turn, - totalTurn: this.nturn, - tiled: this.isReady() ? this.field.field : null, - players: players, - log: this.log, - }; - } } class Player { - public id: string; - public spec: string; - public game: T | null; - public actions: Action[]; - public index: number; - public agents: Agent[]; + id: string; + spec: string; + game: T | null; + actions: Action[]; + index: number; + agents: Agent[]; constructor(id: string, spec = "") { this.id = id; @@ -859,36 +650,36 @@ class Player { this.agents = []; } - static restore(data: Player, game?: Game): Player { + static fromJSON(data: PlayerJson, game?: Game): Player { const player = new Player(data.id, data.spec); player.index = data.index; if (game) { player.game = game; player.agents = data.agents.map((a) => { - return Agent.restore(a, game.board, game.field); + return Agent.fromJSON(a, player.index, game.field); }); } return player; } - toLogJSON(): Player { - const p = { ...this }; - p.game = null; - p.agents = p.agents.map((a) => a.toLogJSON()); - return p; + toJSON(): PlayerJson { + return { + id: this.id, + spec: this.spec, + index: this.index, + actions: this.actions, + agents: this.agents, + }; } setGame(game: T): void { this.game = game; - for (let j = 0; j < game.board.nagent; j++) { - this.agents.push(new Agent(game.board, game.field, this.index)); + for (let j = 0; j < game.field.nAgent; j++) { + this.agents.push(new Agent(game.field, this.index)); } } - noticeStart(): void { - } - setActions(actions: Action[]): typeof Game.prototype.turn { if (this.game === null) throw new Error("game is null"); this.actions = actions; @@ -902,67 +693,6 @@ class Player { clearActions(): void { this.actions = []; } - - getJSON(): { - userId: typeof Player.prototype.id; - spec: typeof Player.prototype.spec; - index: typeof Player.prototype.index; - } { - return { - userId: this.id, - spec: this.spec, - index: this.index, - }; - } -} - -class Kakomimasu { - public games: T[]; - public boards: Board[]; - - constructor() { - this.games = []; - this.boards = []; - } - - appendBoard(board: Board): void { - this.boards.push(board); - } - - getBoards(): typeof Kakomimasu.prototype.boards { - return this.boards; - } - - /** - * @deprecated use addGame - */ - createGame(...param: ConstructorParameters): Game { - //console.log(board); - const game = new Game(...param); - this.games.push(game as T); - return game; - } - - addGame(game: T) { - this.games.push(game); - return game; - } - - getGames() { - return this.games; - } - - getFreeGames() { - return this.games.filter((g) => g.isFree()); - } - - /** - * @deprecated use Player class - */ - createPlayer(playername: string, spec = ""): Player { - if (spec == null) return new Player(playername); - else return new Player(playername, spec); - } } -export { Action, Agent, Board, Field, Game, Kakomimasu, Player }; +export { Action, Agent, Field, Game, Player }; diff --git a/src/json_type.ts b/src/json_type.ts new file mode 100644 index 0000000..08d7924 --- /dev/null +++ b/src/json_type.ts @@ -0,0 +1,45 @@ +import type { + ActionRes, + ActionType, + FieldInit, + FieldTile, + Point, +} from "./Kakomimasu.ts"; + +export interface AgentJson { + x: number; + y: number; +} + +export interface ActionJson { + agentId: number; + type: ActionType; + x: number; + y: number; + res: ActionRes; +} + +export type FieldJson = Required & { + tiles: FieldTile[]; +}; + +export interface PlayerJson { + id: string; + spec: string; + actions: ActionJson[]; + index: number; + agents: AgentJson[]; +} + +export interface GameJson { + turn: number; + totalTurn: number; + field: FieldJson; + players: PlayerJson[]; + log: { + players: { + point: Point; + actions: ActionJson[]; + }[]; + }[]; +} diff --git a/src/test/2d-array_test.ts b/src/test/2d-array_test.ts index b63f401..e833e4d 100644 --- a/src/test/2d-array_test.ts +++ b/src/test/2d-array_test.ts @@ -1,4 +1,4 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assertEquals } from "./deps.ts"; const cl = (...a: Parameters) => { @@ -6,30 +6,28 @@ const cl = (...a: Parameters) => { }; //console.log(...a); Deno.test("two-dimensional array check", () => { - const w = 8; - const h = 8; + const width = 8; + const height = 8; const points = []; - for (let i = 0; i < w * h; i++) points[i] = i; - const nagent = 6; - const nturn = 10; - const board = new Board({ w, h, points, nagent, nturn }); + for (let i = 0; i < width * height; i++) points[i] = i; + const nAgent = 6; + const totalTurn = 10; + const board: Board = { width, height, points, nAgent, totalTurn }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const game = new Game(board); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); for (;;) { - const _st = game.getStatusJSON(); + const _st = game; // console.log(st); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 1, 1], [0, Action.MOVE, 2, 2], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 1, 1], // point 9 [1, Action.PUT, 1, 2], // point 17 [2, Action.PUT, 1, 3], // point 25 @@ -40,15 +38,18 @@ Deno.test("two-dimensional array check", () => { break; } } - assertEquals(game.getStatusJSON().log.length, nturn); - cl(game.getStatusJSON().points); - assertEquals(game.getStatusJSON().points, [{ areaPoint: 0, wallPoint: 0 }, { + assertEquals(game.log.length, totalTurn); + cl(game.field.points); + assertEquals(game.log.at(-1)?.players.map((p) => p.point), [{ + areaPoint: 0, + wallPoint: 0, + }, { areaPoint: 0, wallPoint: 51, }]); // util.p(game.getStatusJSON()); - const log = game.toLogJSON(); + const log = JSON.parse(JSON.stringify(game)); test2dArray(log); }); diff --git a/src/test/action_check_test.ts b/src/test/action_check_test.ts new file mode 100644 index 0000000..36d6b67 --- /dev/null +++ b/src/test/action_check_test.ts @@ -0,0 +1,85 @@ +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; +import { assert, assertEquals, AssertionError } from "./deps.ts"; + +const tos = (game: Game) => { + const isOnAgent = (p: number, x: number, y: number) => { + let cnt = 0; + for (const a of game.players[p].agents) { + if (a.x === x && a.y === y) { + cnt++; + } + } + if (cnt === 1) { + return true; + } + if (cnt === 0) { + return false; + } + throw new AssertionError("agent conflict!! cnt:" + cnt); + }; + + const { height, width } = game.field; + const res = []; + + for (let i = 0; i < height; i++) { + const s = []; + for (let j = 0; j < width; j++) { + const n = game.field.tiles[j + i * width]; + const a0 = isOnAgent(0, j, i); + const a1 = isOnAgent(1, j, i); + if (a0 && a1) { + throw new AssertionError("agent conflict!!"); + } + const a = a0 ? "0" : (a1 ? "1" : "."); + s.push( + "_W".charAt(n.type) + + (n.player === null ? "." : n.player).toString() + + a, + ); + } + res.push(s.join(" ")); + } + return res.join("\n"); +}; + +Deno.test("Action: NONE", () => { + const [width, height] = [3, 1]; + const board: Board = { width, height, points: new Array(width * height) }; + + const game = new Game(board); + + const p1 = new Player("test1"); + const p2 = new Player("test2"); + game.attachPlayer(p1); + game.attachPlayer(p2); + game.start(); + + p1.setActions(Action.fromArray([ + [0, Action.NONE, 0, 0], + ])); + assert(game.nextTurn()); + assertEquals("_.. _.. _..", tos(game)); +}); + +Deno.test("Action: REMOVE checkOnBoard", () => { + const [width, height] = [3, 1]; + const board: Board = { width, height, points: new Array(width * height) }; + + const game = new Game(board); + + const p1 = new Player("test1"); + const p2 = new Player("test2"); + game.attachPlayer(p1); + game.attachPlayer(p2); + game.start(); + + p1.setActions(Action.fromArray([[0, Action.PUT, 2, 0]])); + assert(game.nextTurn()); + assertEquals("_.. _.. W00", tos(game)); + + const action = new Action(0, Action.REMOVE, 3, 0); + p1.setActions([action]); + assert(game.nextTurn()); + assertEquals(action.res, Action.ERR_ILLEGAL_ACTION); + assertEquals("_.. _.. W00", tos(game)); +}); diff --git a/src/test/conflict1_test.ts b/src/test/conflict1_test.ts index 68837f6..a501995 100644 --- a/src/test/conflict1_test.ts +++ b/src/test/conflict1_test.ts @@ -1,20 +1,24 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assert, assertEquals, AssertionError } from "./deps.ts"; Deno.test("conflict1", () => { - const nagent = 6; - const [w, h] = [3, 1]; - const nturn = 20; - const board = new Board({ w, h, points: new Array(w * h), nagent, nturn }); + const nAgent = 6; + const [width, height] = [3, 1]; + const totalTurn = 20; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + totalTurn, + }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const game = new Game(board); const field = game.field; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -37,10 +41,10 @@ Deno.test("conflict1", () => { const tos = () => { const res = []; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -65,10 +69,10 @@ Deno.test("conflict1", () => { // put cl("put"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 2, 0], ])); assert(game.nextTurn()); @@ -76,10 +80,10 @@ Deno.test("conflict1", () => { chk("W00 _.. W11"); cl("move conflict"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); assert(game.nextTurn()); @@ -87,10 +91,10 @@ Deno.test("conflict1", () => { chk("W00 _.. W11"); // move conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); assert(game.nextTurn()); @@ -98,10 +102,10 @@ Deno.test("conflict1", () => { chk("W00 _.. W11"); // put move conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [1, Action.PUT, 1, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); assert(game.nextTurn()); @@ -109,28 +113,28 @@ Deno.test("conflict1", () => { chk("W00 _.. W11"); // move no conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("W0. W00 W11"); // move no conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 0, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("W00 W0. W11"); // move remove conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.REMOVE, 1, 0], ])); assert(game.nextTurn()); @@ -138,8 +142,8 @@ Deno.test("conflict1", () => { chk("W00 W0. W11"); // remove no conflict - p1.setActions(Action.fromJSON([])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([])); + p2.setActions(Action.fromArray([ [0, Action.REMOVE, 1, 0], ])); assert(game.nextTurn()); @@ -147,8 +151,8 @@ Deno.test("conflict1", () => { chk("W00 _.. W11"); cl("move no conflict"); - p1.setActions(Action.fromJSON([])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([])); + p2.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); assert(game.nextTurn()); @@ -156,8 +160,8 @@ Deno.test("conflict1", () => { chk("W00 W11 W1."); cl("remove failed"); - p1.setActions(Action.fromJSON([])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([])); + p2.setActions(Action.fromArray([ [0, Action.REMOVE, 0, 0], ])); assert(game.nextTurn()); @@ -165,8 +169,8 @@ Deno.test("conflict1", () => { chk("W00 W11 W1."); cl("move failed"); - p1.setActions(Action.fromJSON([])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([])); + p2.setActions(Action.fromArray([ [0, Action.MOVE, 0, 0], ])); assert(game.nextTurn()); @@ -174,10 +178,10 @@ Deno.test("conflict1", () => { chk("W00 W11 W1."); cl("cross move failed"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 0, 0], ])); assert(game.nextTurn()); diff --git a/src/test/conflict2_test.ts b/src/test/conflict2_test.ts index 92b84b4..18c292a 100644 --- a/src/test/conflict2_test.ts +++ b/src/test/conflict2_test.ts @@ -1,20 +1,24 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assert, assertEquals, AssertionError } from "./deps.ts"; Deno.test("conflict2", () => { - const nagent = 6; - const [w, h] = [3, 3]; - const nturn = 20; - const board = new Board({ w, h, points: new Array(w * h), nagent, nturn }); + const nAgent = 6; + const [width, height] = [3, 3]; + const totalTurn = 20; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + totalTurn, + }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const game = new Game(board); const field = game.field; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -37,10 +41,10 @@ Deno.test("conflict2", () => { const tos = () => { const res = []; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -63,12 +67,12 @@ Deno.test("conflict2", () => { const chk = (s: string) => assertEquals(s.trim(), tos()); // put - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 0, 1], [2, Action.PUT, 0, 2], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 2, 0], [1, Action.PUT, 2, 1], [2, Action.PUT, 2, 2], @@ -82,12 +86,12 @@ W00 _.. W11 `); // move conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 1], [1, Action.MOVE, 1, 1], [2, Action.MOVE, 1, 1], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 1, 1], [1, Action.MOVE, 1, 1], [2, Action.MOVE, 1, 1], @@ -101,13 +105,13 @@ W00 _.. W11 `); // move remove put conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 1], [1, Action.REMOVE, 1, 1], [2, Action.REMOVE, 1, 1], [3, Action.PUT, 1, 1], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 1, 1], [1, Action.REMOVE, 1, 1], [2, Action.MOVE, 1, 1], @@ -122,12 +126,12 @@ W00 _.. W11 `); // move no conflict - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], [1, Action.MOVE, 1, 1], [2, Action.MOVE, 1, 2], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk(` @@ -137,12 +141,12 @@ W0. W00 W11 `); // move no other wall and agent - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], [1, Action.MOVE, 2, 1], [2, Action.MOVE, 2, 2], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk(` diff --git a/src/test/conflict3_test.ts b/src/test/conflict3_test.ts index 156c8c3..5482e29 100644 --- a/src/test/conflict3_test.ts +++ b/src/test/conflict3_test.ts @@ -1,28 +1,24 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assert, assertEquals, AssertionError } from "./deps.ts"; Deno.test("conflict3", () => { - const nagent = 2; - const nturn = 20; - const nsec = 3; - const [w, h] = [3, 1]; - const board = new Board({ - w, - h, - points: new Array(w * h), - nagent, - nturn, - nsec, - }); - - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const nAgent = 2; + const totalTurn = 20; + const [width, height] = [3, 1]; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + totalTurn, + }; + + const game = new Game(board); const field = game.field; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -59,10 +55,10 @@ Deno.test("conflict3", () => { const tos = () => { const res = []; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -84,7 +80,7 @@ Deno.test("conflict3", () => { const chk = (s: string) => assertEquals(s.trim(), tos()); cl("put"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], ])); assert(game.nextTurn()); @@ -92,7 +88,7 @@ Deno.test("conflict3", () => { chk("W00 _.. _.."); cl("move"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); assert(game.nextTurn()); @@ -100,18 +96,18 @@ Deno.test("conflict3", () => { chk("W0. W00 _.."); cl("put conflict myself"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [1, Action.PUT, 1, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("W0. W00 _.."); showAgents(); cl("put conflict"); - p1.setActions(Action.fromJSON([])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([])); + p2.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], ])); assert(game.nextTurn()); @@ -120,21 +116,21 @@ Deno.test("conflict3", () => { showAgents(); cl("move put conflict myself"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], [1, Action.PUT, 2, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("W0. W00 _.."); showAgents(); cl("move put conflict"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 2, 0], ])); assert(game.nextTurn()); @@ -143,8 +139,8 @@ Deno.test("conflict3", () => { showAgents(); cl("put no conflict"); - p1.setActions(Action.fromJSON([])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([])); + p2.setActions(Action.fromArray([ [0, Action.PUT, 2, 0], ])); assert(game.nextTurn()); @@ -153,31 +149,31 @@ Deno.test("conflict3", () => { showAgents(); cl("remove"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.REMOVE, 0, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("_.. W00 W11"); showAgents(); cl("put"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [1, Action.PUT, 0, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("W00 W00 W11"); showAgents(); cl("remove move conflict"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 0, 0], [1, Action.REMOVE, 1, 0], ])); - p2.setActions(Action.fromJSON([])); + p2.setActions(Action.fromArray([])); assert(game.nextTurn()); p(); chk("W00 W00 W11"); diff --git a/src/test/conflict4_test.ts b/src/test/conflict4_test.ts index 38e197f..536aaa4 100644 --- a/src/test/conflict4_test.ts +++ b/src/test/conflict4_test.ts @@ -1,28 +1,24 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assert, assertEquals, AssertionError } from "./deps.ts"; Deno.test("conflict4", () => { - const nagent = 2; - const nturn = 30; - const nsec = 3; - const [w, h] = [3, 2]; - const board = new Board({ - w, - h, - points: new Array(w * h), - nagent, - nturn, - nsec, - }); + const nAgent = 2; + const totalTurn = 30; + const [width, height] = [3, 2]; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + totalTurn, + }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const game = new Game(board); const field = game.field; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -61,10 +57,10 @@ Deno.test("conflict4", () => { const tos = () => { const res = []; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -86,11 +82,11 @@ Deno.test("conflict4", () => { const chk = (s: string) => assertEquals(s.trim(), tos()); cl("put"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 1, 1], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 1, 0], ])); assert(game.nextTurn()); @@ -101,7 +97,7 @@ _.. W00 _.. `); cl("move"); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], ])); assert(game.nextTurn()); @@ -112,7 +108,7 @@ _.. W00 _.. `); cl("remove move conflict"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.REMOVE, 1, 0], [1, Action.MOVE, 1, 0], ])); @@ -124,7 +120,7 @@ _.. W00 _.. `); cl("move"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); assert(game.nextTurn()); @@ -135,7 +131,7 @@ _.. W00 _.. `); cl("move remove conflict myself"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 0, 0], [0, Action.REMOVE, 0, 0], ])); diff --git a/src/test/conflict5_test.ts b/src/test/conflict5_test.ts index c2221c2..122b16b 100644 --- a/src/test/conflict5_test.ts +++ b/src/test/conflict5_test.ts @@ -1,33 +1,19 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assert, assertEquals, AssertionError } from "./deps.ts"; -/*const assert = (b) => { - assertEquals(true, b); -};*/ - -/* -Deno.test("a", async () => { - assert(true); - assert(true); - assert(false); - assert(true); -}); -*/ Deno.test("conflict5 test", () => { - const nagent = 2; - const [w, h] = [3, 1]; + const nAgent = 2; + const [width, height] = [3, 1]; const points = [1, 1, 1]; - const nturn = 20; - const board = new Board({ w, h, points, nagent, nturn }); + const totalTurn = 20; + const board: Board = { width, height, points, nAgent, totalTurn }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const game = new Game(board); const field = game.field; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -66,10 +52,10 @@ Deno.test("conflict5 test", () => { const tos = () => { const res = []; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -93,7 +79,7 @@ Deno.test("conflict5 test", () => { //kc.setActions([ new Action(0, "MOVE", 4, 2),new Action(0, "MOVE", 3, 2)]); // こっちだとAgentが[3,2]に移動する。 //kc.setActions([ new Action(0, "MOVE", 3, 2),new Action(0, "MOVE", 4, 2)]); // こっちだとAgentは移動しない。 cl("put"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], ])); assert(game.nextTurn()); @@ -104,7 +90,7 @@ Deno.test("conflict5 test", () => { let actions; cl("conflict1"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], [0, Action.MOVE, 2, 0], ])); @@ -119,7 +105,7 @@ Deno.test("conflict5 test", () => { chk("W00 _.. _.."); cl("conflict2"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], [0, Action.MOVE, 1, 0], ])); @@ -134,7 +120,7 @@ Deno.test("conflict5 test", () => { //console.log(game.log); cl("conflict3"); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], [0, Action.MOVE, 1, 0], [0, Action.MOVE, 3, 0], diff --git a/src/test/field_test.ts b/src/test/field_test.ts new file mode 100644 index 0000000..269d5a4 --- /dev/null +++ b/src/test/field_test.ts @@ -0,0 +1,24 @@ +import { Board, Field } from "../Kakomimasu.ts"; +import { assertThrows } from "./deps.ts"; + +Deno.test("Field: set invalid playerid", () => { + const [width, height] = [3, 1]; + const board: Board = { width, height, points: new Array(width * height) }; + + const field = new Field(board); + + assertThrows(() => { + field.set(0, 0, Field.WALL, -1); + }); +}); + +Deno.test("Field: invalid points.length", () => { + const fn = () => { + new Field({ + width: 3, + height: 3, + points: new Array(2).fill(0), + }); + }; + assertThrows(fn, Error, "points.length must be 9"); +}); diff --git a/src/test/fillBase1_test.ts b/src/test/fillBase1_test.ts index 62b1c9d..96e956a 100644 --- a/src/test/fillBase1_test.ts +++ b/src/test/fillBase1_test.ts @@ -5,17 +5,22 @@ const cl = (...a: Parameters) => { a; }; //console.log(...a); -Deno.test("fillBase1", () => { - const nagent = 6; - const [w, h] = [3, 3]; - const board = new Board({ w, h, points: new Array(w * h), nagent }); +Deno.test("fill1", () => { + const nAgent = 6; + const [width, height] = [3, 3]; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + }; const field = new Field(board); const p = () => { - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; s.push( "_W".charAt(n.type) + (n.player === null ? "." : n.player).toString(), ); @@ -29,9 +34,9 @@ Deno.test("fillBase1", () => { for (let i = 0; i < s.length; i++) { const c = s.charAt(i); if (c === "0") { - field.field[i] = { type: Field.WALL, player: 0 }; + field.tiles[i] = { type: Field.WALL, player: 0 }; } else if (c === "1") { - field.field[i] = { type: Field.WALL, player: 1 }; + field.tiles[i] = { type: Field.WALL, player: 1 }; } } }; @@ -41,7 +46,7 @@ Deno.test("fillBase1", () => { const c = s.charAt(i); if (c !== ".") { const n = parseInt(c); - const f = field.field[i]; + const f = field.tiles[i]; if (f.type !== Field.AREA || f.player !== n) { throw new AssertionError(""); } @@ -59,7 +64,7 @@ Deno.test("fillBase1", () => { p(); - field.fillBase(); + field.fillArea(); p(); diff --git a/src/test/fillBase2_test.ts b/src/test/fillBase2_test.ts index cbd70f3..f6b431c 100644 --- a/src/test/fillBase2_test.ts +++ b/src/test/fillBase2_test.ts @@ -4,17 +4,22 @@ const cl = (...a: Parameters) => { a; }; //console.log(...a); -Deno.test("fillBase2", () => { - const nagent = 6; - const [w, h] = [8, 8]; - const board = new Board({ w, h, points: new Array(w * h), nagent }); +Deno.test("fill2", () => { + const nAgent = 6; + const [width, height] = [8, 8]; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + }; const field = new Field(board); const p = () => { - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; s.push( "_W".charAt(n.type) + (n.player === null ? "." : n.player).toString(), ); @@ -28,11 +33,11 @@ Deno.test("fillBase2", () => { for (let i = 0; i < s.length; i++) { const c = s.charAt(i); if (c === "0") { - field.field[i] = { type: Field.WALL, player: 0 }; + field.tiles[i] = { type: Field.WALL, player: 0 }; } else if (c === "1") { - field.field[i] = { type: Field.WALL, player: 1 }; + field.tiles[i] = { type: Field.WALL, player: 1 }; } else { - field.field[i] = { type: Field.AREA, player: null }; + field.tiles[i] = { type: Field.AREA, player: null }; } } }; @@ -42,7 +47,7 @@ Deno.test("fillBase2", () => { const c = s.charAt(i); if (c !== ".") { const n = parseInt(c); - const f = field.field[i]; + const f = field.tiles[i]; if (f.type !== Field.AREA || f.player !== n) { throw new Error(); } @@ -65,7 +70,7 @@ Deno.test("fillBase2", () => { `); p(); - field.fillBase(); + field.fillArea(); p(); @@ -94,7 +99,7 @@ Deno.test("fillBase2", () => { `); p(); - field.fillBase(); + field.fillArea(); p(); chk(` @@ -122,7 +127,7 @@ Deno.test("fillBase2", () => { `); p(); - field.fillBase(); + field.fillArea(); p(); chk(` diff --git a/src/test/fillBase3_test.ts b/src/test/fillBase3_test.ts index 21c5fbd..7a8f245 100644 --- a/src/test/fillBase3_test.ts +++ b/src/test/fillBase3_test.ts @@ -5,17 +5,22 @@ const cl = (...a: Parameters) => { a; }; //console.log(...a); -Deno.test("fillBase2", () => { - const nagent = 6; - const [w, h] = [3, 3]; - const board = new Board({ w, h, points: new Array(w * h), nagent }); +Deno.test("fill2", () => { + const nAgent = 6; + const [width, height] = [3, 3]; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + }; const field = new Field(board); const p = () => { - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; s.push( "_W".charAt(n.type) + (n.player === null ? "." : n.player).toString(), ); @@ -29,9 +34,9 @@ Deno.test("fillBase2", () => { for (let i = 0; i < s.length; i++) { const c = s.charAt(i); if (c === "0") { - field.field[i] = { type: Field.WALL, player: 0 }; + field.tiles[i] = { type: Field.WALL, player: 0 }; } else if (c === "1") { - field.field[i] = { type: Field.WALL, player: 1 }; + field.tiles[i] = { type: Field.WALL, player: 1 }; } } }; @@ -40,7 +45,7 @@ Deno.test("fillBase2", () => { for (let i = 0; i < s.length; i += 2) { const c = s.charAt(i) === "W" ? Field.WALL : Field.AREA; const n = s.charAt(i + 1) === "." ? null : parseInt(s.charAt(i + 1)); - const f = field.field[i / 2]; + const f = field.tiles[i / 2]; if (f.type !== c || f.player !== n) { throw new AssertionError(""); } @@ -57,7 +62,7 @@ Deno.test("fillBase2", () => { p(); - field.fillBase(); + field.fillArea(); p(); diff --git a/src/test/flow_test.ts b/src/test/flow_test.ts index 0022f09..4add2c7 100644 --- a/src/test/flow_test.ts +++ b/src/test/flow_test.ts @@ -1,4 +1,4 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assertEquals } from "./deps.ts"; //import util from "../util.mjs"; @@ -8,34 +8,32 @@ const cl = (...a: Parameters) => { Deno.test("flow", () => { //test("flow", () => { - const w = 8; - const h = 8; + const width = 8; + const height = 8; const points = []; - for (let i = 0; i < w * h; i++) { + for (let i = 0; i < width * height; i++) { points[i] = i; // points[i] = i % (16 * 2 + 1) - 16; // util.rnd(16 * 2 + 1) - 16; } - const nagent = 6; - const nturn = 10; - const board = new Board({ w, h, points, nagent, nturn }); + const nAgent = 6; + const totalTurn = 10; + const board: Board = { width, height, points, nAgent, totalTurn }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const game = new Game(board); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); for (;;) { - const _st = game.getStatusJSON(); + const _st = game; // console.log(st); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 1, 1], [0, Action.MOVE, 2, 2], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 1, 1], // point 9 [1, Action.PUT, 1, 2], // point 17 [2, Action.PUT, 1, 3], // point 25 @@ -46,9 +44,12 @@ Deno.test("flow", () => { break; } } - assertEquals(game.getStatusJSON().log.length, nturn); - cl(game.getStatusJSON().points); - assertEquals(game.getStatusJSON().points, [{ areaPoint: 0, wallPoint: 0 }, { + assertEquals(game.log.length, totalTurn); + cl(game.field.points); + assertEquals(game.log.at(-1)?.players.map((p) => p.point), [{ + areaPoint: 0, + wallPoint: 0, + }, { areaPoint: 0, wallPoint: 51, }]); diff --git a/src/test/game_check_test.ts b/src/test/game_check_test.ts new file mode 100644 index 0000000..fa3521f --- /dev/null +++ b/src/test/game_check_test.ts @@ -0,0 +1,56 @@ +import { Board, Game, Player } from "../Kakomimasu.ts"; +import { assertEquals } from "./deps.ts"; + +Deno.test("Game: too much attach", () => { + const [width, height] = [3, 1]; + const board: Board = { width, height, points: new Array(width * height) }; + + const game = new Game(board); + + assertEquals(game.attachPlayer(new Player("test1")), true); + assertEquals(game.attachPlayer(new Player("test2")), true); + assertEquals(game.attachPlayer(new Player("test3")), false); +}); + +Deno.test("Game: attach same player", () => { + const [width, height] = [3, 1]; + const board: Board = { width, height, points: new Array(width * height) }; + + const game = new Game(board); + + const p = new Player("test1"); + + assertEquals(game.attachPlayer(p), true); + assertEquals(game.attachPlayer(p), false); +}); + +Deno.test("Game: status check", () => { + const [width, height] = [3, 1]; + const board: Board = { width, height, points: new Array(width * height) }; + + const game = new Game(board); + + const check = ( + isFree: boolean, + isReady: boolean, + isGaming: boolean, + isEnded: boolean, + ) => { + assertEquals(game.isFree(), isFree); + assertEquals(game.isReady(), isReady); + assertEquals(game.isGaming(), isGaming); + assertEquals(game.isEnded(), isEnded); + }; + + check(true, false, false, false); + game.attachPlayer(new Player("test1")); + check(true, false, false, false); + game.attachPlayer(new Player("test2")); + check(false, true, false, false); + game.start(); + check(false, false, true, false); + while (game.nextTurn()) { + check(false, false, true, false); + } + check(false, false, false, true); +}); diff --git a/src/test/player_check_test.ts b/src/test/player_check_test.ts new file mode 100644 index 0000000..f2ba99e --- /dev/null +++ b/src/test/player_check_test.ts @@ -0,0 +1,8 @@ +import { Player } from "../Kakomimasu.ts"; +import { assertThrows } from "./deps.ts"; + +Deno.test("Player: setActions before game attached", () => { + const p1 = new Player("test1"); + const fn = () => p1.setActions([]); + assertThrows(fn, Error, "game is null"); +}); diff --git a/src/test/random_test.ts b/src/test/random_test.ts index 0ce40f4..ea9f027 100644 --- a/src/test/random_test.ts +++ b/src/test/random_test.ts @@ -1,9 +1,10 @@ import { Action, - ActionJSON, + ActionArray, ActionType, Board, - Kakomimasu, + Game, + Player, } from "../Kakomimasu.ts"; import { assert, AssertionError } from "./deps.ts"; //import util from "../util.js"; @@ -15,22 +16,26 @@ const cl = (...a: Parameters) => { }; //console.log(...a); Deno.test("random", () => { - const nagent = 6; - const [w, h] = [nagent, nagent]; - const nturn = 10000; - const board = new Board({ w, h, points: new Array(w * h), nagent, nturn }); + const nAgent = 6; + const [width, height] = [nAgent, nAgent]; + const totalTurn = 10000; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + totalTurn, + }; const initialput = false; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const game = new Game(board); const field = game.field; const nplayers = 2; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -66,10 +71,10 @@ Deno.test("random", () => { const tos = () => { const res = []; let fillfld = 0; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -93,9 +98,9 @@ Deno.test("random", () => { return res.join("\n"); }; const checkAgent = () => { - for (let i = 0; i < h; i++) { - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let i = 0; i < height; i++) { + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -123,8 +128,8 @@ Deno.test("random", () => { // put let actions: ActionType[]; const getPutAction = (x: number) => { - const act: ActionJSON[] = []; - for (let i = 0; i < nagent; i++) { + const act: ActionArray[] = []; + for (let i = 0; i < nAgent; i++) { act.push([i, Action.PUT, x, i]); } return act; @@ -132,8 +137,8 @@ Deno.test("random", () => { game.nextTurn(); chk(); if (initialput) { - p1.setActions(Action.fromJSON(getPutAction(0))); - p2.setActions(Action.fromJSON(getPutAction(nagent - 1))); + p1.setActions(Action.fromArray(getPutAction(0))); + p2.setActions(Action.fromArray(getPutAction(nAgent - 1))); assert(game.nextTurn()); chk(); actions = [Action.MOVE, Action.REMOVE]; @@ -141,24 +146,24 @@ Deno.test("random", () => { actions = [Action.PUT, Action.MOVE, Action.REMOVE]; } //console.log("actions", actions); - const getRandomAction = (n: number): ActionJSON => [ + const getRandomAction = (n: number): ActionArray => [ n, actions[util.rnd(actions.length)], - util.rnd(nagent), - util.rnd(nagent), + util.rnd(nAgent), + util.rnd(nAgent), ]; - for (let i = 1; i <= nturn; i++) { - const act: ActionJSON[][] = []; + for (let i = 1; i <= totalTurn; i++) { + const act: ActionArray[][] = []; for (let k = 0; k < nplayers; k++) { - const act2: ActionJSON[] = []; - for (let j = 0; j < nagent; j++) { + const act2: ActionArray[] = []; + for (let j = 0; j < nAgent; j++) { act2.push(getRandomAction(j)); } act.push(act2); } //console.log("act", act); - p1.setActions(Action.fromJSON(act[0])); - p2.setActions(Action.fromJSON(act[1])); + p1.setActions(Action.fromArray(act[0])); + p2.setActions(Action.fromArray(act[1])); if (i === 9) { showAgents(); } diff --git a/src/test/restore_test.ts b/src/test/restore_test.ts index a373fb7..7dc030e 100644 --- a/src/test/restore_test.ts +++ b/src/test/restore_test.ts @@ -1,25 +1,18 @@ import { assert, assertEquals } from "./deps.ts"; import { Action, Agent, Board, Field, Game, Player } from "../Kakomimasu.ts"; -const boardObj: ConstructorParameters[0] = { - w: 2, - h: 2, +const boardObj: Board = { + width: 2, + height: 2, points: [1, 2, 3, 4], - nagent: 1, - nturn: 10, - nsec: 1, - nplayer: 2, + nAgent: 1, + nPlayer: 2, + totalTurn: 10, }; -function assertBoard(a: Board, b: Board) { - assertEquals(a, b); -} - function assertAgent(a: Agent, b: Agent) { assertEquals(a, b); - assert(a.board === b.board); // check same instance assert(a.field === b.field); // check same instance - assertEquals(a.lastaction, b.lastaction); } function assertAction(a: Action, b: Action) { @@ -28,11 +21,22 @@ function assertAction(a: Action, b: Action) { function assertField(a: Field, b: Field) { assertEquals(a, b); - assert(a.board === b.board); // check same instance } function assertGame(a: Game, b: Game) { - assertEquals(a, b); + // Player.gameが循環参照になるので分けて比較 + const { players: aPlayers, ...aOther } = a; + const { players: bPlayers, ...bOther } = b; + assertEquals(aOther, bOther); + assertEquals(aPlayers.length, bPlayers.length); + for (let i = 0; i < aPlayers.length; i++) { + const { game: aGame, ...aOther } = aPlayers[i]; + const { game: bGame, ...bOther } = bPlayers[i]; + if (aGame) aGame.turn = 1; + assert(aGame === a); // check same instance + assert(bGame === b); // check same instance + assertEquals(aOther, bOther); + } } function assertPlayer(a: Player, b: Player) { @@ -40,105 +44,76 @@ function assertPlayer(a: Player, b: Player) { assert(a.game === b.game); // check same instance } -// Board test -// constructor paramを変えてテスト -Deno.test("restore Board class with all param", () => { - const board = new Board(boardObj); - const restoredBoard = Board.restore(board.toLogJSON()); - - assertEquals(board, restoredBoard); -}); -Deno.test("restore Board class with no 'nturn'", () => { - const { nturn: _, ...b } = boardObj; - const board = new Board(b); - const restoredBoard = Board.restore(board.toLogJSON()); - - assertBoard(board, restoredBoard); -}); -Deno.test("restore Board class with no 'nsec'", () => { - const { nsec: _, ...b } = boardObj; - const board = new Board(b); - const restoredBoard = Board.restore(board.toLogJSON()); - - assertBoard(board, restoredBoard); -}); -Deno.test("restore Board class with no 'nplayer'", () => { - const { nplayer: _, ...b } = boardObj; - const board = new Board(b); - const restoredBoard = Board.restore(board.toLogJSON()); - - assertBoard(board, restoredBoard); -}); -Deno.test("restore Board class with no 'name'", () => { - const { name: _, ...b } = boardObj; - const board = new Board(b); - const restoredBoard = Board.restore(board.toLogJSON()); - - assertBoard(board, restoredBoard); -}); - // Agent test // lastactionを変えてテスト -Deno.test("restore Agent class with lastaction is null", () => { - const board = new Board(boardObj); - const field = new Field(board); - const agent = new Agent(board, field, 1); - const restoredAgent = Agent.restore(agent.toLogJSON(), board, field); - - assertAgent(agent, restoredAgent); -}); -Deno.test("restore Agent class with lastaction is Action class", () => { - const board = new Board(boardObj); - const field = new Field(board); - const agent = new Agent(board, field, 1); - const action = new Action(0, Action.NONE, 0, 0); - agent.lastaction = action; - const restoredAgent = Agent.restore(agent.toLogJSON(), board, field); +Deno.test("fromJSON Agent class with lastaction is null", () => { + const field = new Field(boardObj); + const agent = new Agent(field, 1); + const restoredAgent = Agent.fromJSON( + JSON.parse(JSON.stringify(agent)), + 1, + field, + ); assertAgent(agent, restoredAgent); }); // Action test -Deno.test("restore Action class", () => { +Deno.test("fromJSON Action class", () => { const action = new Action(0, Action.NONE, 0, 0); - const restoredAction = Action.restore(action); + const restoredAction = Action.fromJSON(JSON.parse(JSON.stringify(action))); assertAction(action, restoredAction); }); // Field test -Deno.test("restore Field class", () => { - const board = new Board(boardObj); - const field = new Field(board); - const restoredField = Field.restore(field.toLogJSON(), board); +Deno.test("fromJSON Field class", () => { + const field = new Field(boardObj); + const restoredField = Field.fromJSON(JSON.parse(JSON.stringify(field))); assertField(field, restoredField); }); // Game test -Deno.test("restore Game class", () => { - const board = new Board(boardObj); - const game = new Game(board); - const restoredGame = Game.restore(game.toLogJSON()); +Deno.test("fromJSON Game class", () => { + const game = new Game(boardObj); + const restoredGame = Game.fromJSON(JSON.parse(JSON.stringify(game))); + + assertGame(game, restoredGame); +}); +Deno.test("fromJSON Game class with custom", () => { + const game = new Game({ ...boardObj, nPlayer: 3, totalTurn: 20 }); + const restoredGame = Game.fromJSON(JSON.parse(JSON.stringify(game))); + + assertGame(game, restoredGame); +}); +Deno.test("fromJSON Game class with players", () => { + const game = new Game(boardObj); + game.attachPlayer(new Player("abcd", "spec")); + game.attachPlayer(new Player("efgh", "spec")); + const restoredGame = Game.fromJSON(JSON.parse(JSON.stringify(game))); assertGame(game, restoredGame); }); // Player test // game有り無しでテスト -Deno.test("restore Player class with game is null", () => { +Deno.test("fromJSON Player class with game is null", () => { const player = new Player("abcd", "spec"); - const restoredPlayer = Player.restore(player.toLogJSON()); + const restoredPlayer = Player.fromJSON(JSON.parse(JSON.stringify(player))); assertPlayer(player, restoredPlayer); }); -Deno.test("restore Player class with game is class", () => { - const board = new Board(boardObj); +Deno.test("fromJSON Player class with game is class", () => { + const board: Board = boardObj; const game = new Game(board); const player = new Player("abcd", "spec"); player.setGame(game); - const restoredPlayer = Player.restore(player.toLogJSON(), game); + const restoredPlayer = Player.fromJSON( + JSON.parse(JSON.stringify(player)), + game, + ); assertPlayer(player, restoredPlayer); }); diff --git a/src/test/revert1_test.ts b/src/test/revert1_test.ts index 890b2a2..3873735 100644 --- a/src/test/revert1_test.ts +++ b/src/test/revert1_test.ts @@ -1,4 +1,4 @@ -import { Action, Board, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Game, Player } from "../Kakomimasu.ts"; import { assert, assertEquals, AssertionError } from "./deps.ts"; const cl = (...a: Parameters) => { @@ -6,19 +6,23 @@ const cl = (...a: Parameters) => { }; //console.log(...a); Deno.test("revert1", () => { - const nagent = 6; - const [w, h] = [3, 1]; - const nturn = 20; - const board = new Board({ w, h, points: new Array(w * h), nagent, nturn }); + const nAgent = 6; + const [width, height] = [3, 1]; + const totalTurn = 20; + const board: Board = { + width, + height, + points: new Array(width * height), + nAgent, + totalTurn, + }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); + const game = new Game(board); const field = game.field; - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -41,10 +45,10 @@ Deno.test("revert1", () => { const tos = () => { const res = []; - for (let i = 0; i < h; i++) { + for (let i = 0; i < height; i++) { const s = []; - for (let j = 0; j < w; j++) { - const n = field.field[j + i * w]; + for (let j = 0; j < width; j++) { + const n = field.tiles[j + i * width]; const a0 = isOnAgent(0, j, i); const a1 = isOnAgent(1, j, i); if (a0 && a1) { @@ -65,7 +69,7 @@ Deno.test("revert1", () => { const chk = (s: string) => assertEquals(s.trim(), tos()); // put - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 1, 0], ])); @@ -74,7 +78,7 @@ Deno.test("revert1", () => { chk("W00 W00 _.."); // move x2 - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], [1, Action.MOVE, 2, 0], ])); @@ -83,7 +87,7 @@ Deno.test("revert1", () => { chk("W0. W00 W00"); // move x2 revert - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 2, 0], [1, Action.MOVE, 3, 0], ])); diff --git a/src/test/sample_test.ts b/src/test/sample_test.ts new file mode 100644 index 0000000..a362db0 --- /dev/null +++ b/src/test/sample_test.ts @@ -0,0 +1,3 @@ +Deno.test("sample/main.js", async () => { + await import("../../sample/main.js"); +}); diff --git a/src/test/unit_test.ts b/src/test/unit_test.ts index c75212d..3a39ff0 100644 --- a/src/test/unit_test.ts +++ b/src/test/unit_test.ts @@ -1,24 +1,22 @@ -import { Action, Board, Field, Kakomimasu } from "../Kakomimasu.ts"; +import { Action, Board, Field, Game, Player } from "../Kakomimasu.ts"; import { assertEquals } from "./deps.ts"; // import util from "../util.mjs"; const prepare = () => { - const w = 3; - const h = 3; + const width = 3; + const height = 3; const points = []; - for (let i = 0; i < w * h; i++) { + for (let i = 0; i < width * height; i++) { points[i] = i; // points[i] = i % (16 * 2 + 1) - 16; // util.rnd(16 * 2 + 1) - 16; } - const nagent = 9; - const board = new Board({ w, h, points, nagent, nturn: 30 }); + const nAgent = 9; + const board: Board = { width, height, points, nAgent, totalTurn: 30 }; - const kkmm = new Kakomimasu(); - kkmm.appendBoard(board); - const game = kkmm.createGame(board); - const p1 = kkmm.createPlayer("test1"); - const p2 = kkmm.createPlayer("test2"); + const game = new Game(board); + const p1 = new Player("test1"); + const p2 = new Player("test2"); game.attachPlayer(p1); game.attachPlayer(p2); game.start(); @@ -27,67 +25,64 @@ const prepare = () => { Deno.test("action put", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); game.nextTurn(); - const status = game.getStatusJSON(); - assertEquals(status.field[0], { type: Field.WALL, player: 0 }); + assertEquals(game.field.tiles[0], { type: Field.WALL, player: 0 }); }); Deno.test("action can't put", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 1000, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 1000, 0]])); game.nextTurn(); - const status = game.getStatusJSON(); - assertEquals(status.field[0], { type: Field.AREA, player: null }); + assertEquals(game.field.tiles[0], { type: Field.AREA, player: null }); assertEquals( - status.log[0].players[0].actions[0].res, + game.log[0].players[0].actions[0].res, Action.ERR_ILLEGAL_ACTION, ); }); Deno.test("action move", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.MOVE, 1, 0]])); - const status = game.getStatusJSON(); - assertEquals(status.field[0], { type: Field.WALL, player: 0 }); + p1.setActions(Action.fromArray([[0, Action.MOVE, 1, 0]])); + assertEquals(game.field.tiles[0], { type: Field.WALL, player: 0 }); }); Deno.test("action move series", () => { //Deno.stdout.writeSync(new TextEncoder().encode("連なり移動")); console.log("連なり移動"); const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 1, 0], ])); game.nextTurn(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], [1, Action.MOVE, 2, 0], ])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[2], { type: Field.WALL, player: 0 }); + assertEquals(game.field.tiles[2], { type: Field.WALL, player: 0 }); }); Deno.test("action cant't move series", () => { console.log("連なり移動失敗"); const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 1, 0], ])); game.nextTurn(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], [1, Action.MOVE, 2, 0], ])); - p2.setActions(Action.fromJSON([ + p2.setActions(Action.fromArray([ [0, Action.PUT, 2, 0], ])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[2], { + assertEquals(game.field.tiles[2], { type: Field.AREA, player: null, }); @@ -95,21 +90,20 @@ Deno.test("action cant't move series", () => { Deno.test("action can't move", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.MOVE, 2, 0]])); + p1.setActions(Action.fromArray([[0, Action.MOVE, 2, 0]])); game.nextTurn(); - const status = game.getStatusJSON(); - assertEquals(status.field[2], { type: Field.AREA, player: null }); + assertEquals(game.field.tiles[2], { type: Field.AREA, player: null }); assertEquals( - status.log[1].players[0].actions[0].res, + game.log[1].players[0].actions[0].res, Action.ERR_ILLEGAL_ACTION, ); }); Deno.test("fill", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 1, 0], [2, Action.PUT, 2, 0], @@ -120,26 +114,25 @@ Deno.test("fill", () => { [7, Action.PUT, 2, 2], ])); game.nextTurn(); - const status = game.getStatusJSON(); - assertEquals(status.field[4], { type: Field.AREA, player: 0 }); + assertEquals(game.field.tiles[4], { type: Field.AREA, player: 0 }); }); Deno.test("action remove", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], ])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[0], { type: Field.WALL, player: 0 }); - p1.setActions(Action.fromJSON([ + assertEquals(game.field.tiles[0], { type: Field.WALL, player: 0 }); + p1.setActions(Action.fromArray([ [0, Action.MOVE, 1, 0], ])); game.nextTurn(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.REMOVE, 0, 0], ])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[0], { + assertEquals(game.field.tiles[0], { type: Field.AREA, player: null, }); @@ -147,32 +140,32 @@ Deno.test("action remove", () => { Deno.test("action can't remove", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], ])); game.nextTurn(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.REMOVE, 1, 0], ])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[1], { + assertEquals(game.field.tiles[1], { type: Field.AREA, player: null, }); assertEquals( - game.getStatusJSON().log[1].players[0].actions[0].res, + game.log[1].players[0].actions[0].res, Action.ERR_ILLEGAL_ACTION, ); }); Deno.test("wall point", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 1, 0], [1, Action.PUT, 2, 0], ])); game.nextTurn(); - assertEquals(game.getStatusJSON().points[0], { + assertEquals(game.log[0].players[0].point, { areaPoint: 0, wallPoint: 1 + 2, }); @@ -180,7 +173,7 @@ Deno.test("wall point", () => { Deno.test("AREA point", () => { const { game, p1 } = prepare(); - p1.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([ [0, Action.PUT, 0, 0], [1, Action.PUT, 1, 0], [2, Action.PUT, 2, 0], @@ -191,9 +184,9 @@ Deno.test("AREA point", () => { [7, Action.PUT, 2, 2], ])); game.nextTurn(); - const status = game.getStatusJSON(); - assertEquals(status.field[4], { type: Field.AREA, player: 0 }); - assertEquals(game.getStatusJSON().points[0], { + const status = game; + assertEquals(status.field.tiles[4], { type: Field.AREA, player: 0 }); + assertEquals(game.log[0].players[0].point, { areaPoint: 4, wallPoint: 0 + 1 + 2 + 3 + 5 + 6 + 7 + 8, }); @@ -202,66 +195,63 @@ Deno.test("AREA point", () => { Deno.test("remove on agent", () => { console.log("エージェントがいるマスの壁はREMOVE不可"); const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); - p2.setActions(Action.fromJSON([[0, Action.PUT, 1, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); + p2.setActions(Action.fromArray([[0, Action.PUT, 1, 0]])); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.REMOVE, 1, 0]])); + p1.setActions(Action.fromArray([[0, Action.REMOVE, 1, 0]])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[1], { type: Field.WALL, player: 1 }); + assertEquals(game.field.tiles[1], { type: Field.WALL, player: 1 }); assertEquals( - game.getStatusJSON().log[1].players[0].actions[0].res, + game.log[1].players[0].actions[0].res, Action.REVERT, ); }); Deno.test("conflict put", () => { const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); - p2.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); + p2.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); game.nextTurn(); - const status = game.getStatusJSON(); - // util.p(status.agents); - assertEquals(status.field[0], { type: Field.AREA, player: null }); - assertEquals(status.log[0].players[0].actions[0].res, Action.CONFLICT); - assertEquals(status.log[0].players[1].actions[0].res, Action.CONFLICT); + assertEquals(game.field.tiles[0], { type: Field.AREA, player: null }); + assertEquals(game.log[0].players[0].actions[0].res, Action.CONFLICT); + assertEquals(game.log[0].players[1].actions[0].res, Action.CONFLICT); }); Deno.test("conflict move", () => { const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); - p2.setActions(Action.fromJSON([[0, Action.PUT, 2, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); + p2.setActions(Action.fromArray([[0, Action.PUT, 2, 0]])); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.MOVE, 1, 0]])); - p2.setActions(Action.fromJSON([[0, Action.MOVE, 1, 0]])); + p1.setActions(Action.fromArray([[0, Action.MOVE, 1, 0]])); + p2.setActions(Action.fromArray([[0, Action.MOVE, 1, 0]])); game.nextTurn(); - const status = game.getStatusJSON(); // util.p(status.agents); - assertEquals(status.field[1], { type: Field.AREA, player: null }); - assertEquals(status.log[1].players[0].actions[0].res, Action.CONFLICT); - assertEquals(status.log[1].players[1].actions[0].res, Action.CONFLICT); + assertEquals(game.field.tiles[1], { type: Field.AREA, player: null }); + assertEquals(game.log[1].players[0].actions[0].res, Action.CONFLICT); + assertEquals(game.log[1].players[1].actions[0].res, Action.CONFLICT); }); Deno.test("conflict remove", () => { const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); - p2.setActions(Action.fromJSON([ + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); + p2.setActions(Action.fromArray([ [0, Action.PUT, 2, 0], [1, Action.PUT, 1, 0], ])); game.nextTurn(); - p2.setActions(Action.fromJSON([[1, Action.MOVE, 1, 1]])); - assertEquals(game.getStatusJSON().field[1], { type: Field.WALL, player: 1 }); + p2.setActions(Action.fromArray([[1, Action.MOVE, 1, 1]])); + assertEquals(game.field.tiles[1], { type: Field.WALL, player: 1 }); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.REMOVE, 1, 0]])); - p2.setActions(Action.fromJSON([[0, Action.REMOVE, 1, 0]])); + p1.setActions(Action.fromArray([[0, Action.REMOVE, 1, 0]])); + p2.setActions(Action.fromArray([[0, Action.REMOVE, 1, 0]])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[1], { type: Field.WALL, player: 1 }); + assertEquals(game.field.tiles[1], { type: Field.WALL, player: 1 }); assertEquals( - game.getStatusJSON().log[2].players[0].actions[0].res, + game.log[2].players[0].actions[0].res, Action.CONFLICT, ); assertEquals( - game.getStatusJSON().log[2].players[1].actions[0].res, + game.log[2].players[1].actions[0].res, Action.CONFLICT, ); }); @@ -269,19 +259,19 @@ Deno.test("conflict remove", () => { Deno.test("conflict remove & move", () => { console.log("壁がないところの先読みREMOVEは不可、移動が成功する"); const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); - p2.setActions(Action.fromJSON([[0, Action.PUT, 2, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); + p2.setActions(Action.fromArray([[0, Action.PUT, 2, 0]])); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.REMOVE, 1, 0]])); - p2.setActions(Action.fromJSON([[0, Action.MOVE, 1, 0]])); + p1.setActions(Action.fromArray([[0, Action.REMOVE, 1, 0]])); + p2.setActions(Action.fromArray([[0, Action.MOVE, 1, 0]])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[1], { type: Field.WALL, player: 1 }); + assertEquals(game.field.tiles[1], { type: Field.WALL, player: 1 }); assertEquals( - game.getStatusJSON().log[1].players[0].actions[0].res, + game.log[1].players[0].actions[0].res, Action.ERR_ILLEGAL_ACTION, ); assertEquals( - game.getStatusJSON().log[1].players[1].actions[0].res, + game.log[1].players[1].actions[0].res, Action.SUCCESS, ); }); @@ -289,18 +279,18 @@ Deno.test("conflict remove & move", () => { Deno.test("conflict remove & move", () => { console.log("壁がないところの先読みREMOVEは不可、PUTが成功する"); const { game, p1, p2 } = prepare(); - p1.setActions(Action.fromJSON([[0, Action.PUT, 0, 0]])); + p1.setActions(Action.fromArray([[0, Action.PUT, 0, 0]])); game.nextTurn(); - p1.setActions(Action.fromJSON([[0, Action.REMOVE, 1, 0]])); - p2.setActions(Action.fromJSON([[0, Action.PUT, 1, 0]])); + p1.setActions(Action.fromArray([[0, Action.REMOVE, 1, 0]])); + p2.setActions(Action.fromArray([[0, Action.PUT, 1, 0]])); game.nextTurn(); - assertEquals(game.getStatusJSON().field[1], { type: Field.WALL, player: 1 }); + assertEquals(game.field.tiles[1], { type: Field.WALL, player: 1 }); assertEquals( - game.getStatusJSON().log[1].players[0].actions[0].res, + game.log[1].players[0].actions[0].res, Action.ERR_ILLEGAL_ACTION, ); assertEquals( - game.getStatusJSON().log[1].players[1].actions[0].res, + game.log[1].players[1].actions[0].res, Action.SUCCESS, ); }); diff --git a/src/test/util_test.js b/src/test/util_test.js deleted file mode 100644 index 6ed656a..0000000 --- a/src/test/util_test.js +++ /dev/null @@ -1,9 +0,0 @@ -import util from "../../util.js"; -import { assert } from "./deps.ts"; - -Deno.test("rnd", () => { - for (let i = 0; i < 100; i++) { - const n = util.rnd(10); - assert(n >= 0 && n < 10); - } -}); diff --git a/types/Kakomimasu.d.ts b/types/Kakomimasu.d.ts index 10afe30..8592eca 100644 --- a/types/Kakomimasu.d.ts +++ b/types/Kakomimasu.d.ts @@ -1,90 +1,39 @@ +import type { ActionJson, AgentJson, FieldJson, GameJson, PlayerJson } from "./json_type.js"; export declare type Point = { areaPoint: number; wallPoint: number; }; -declare class Board { - w: number; - h: number; +export interface Board { + width: number; + height: number; points: number[]; - nagent: number; - nturn: number; - nsec: number; - nplayer: number; - name: string; - constructor({ w, h, points, nagent, nturn, nsec, nplayer, name }: { - w: number; - h: number; - points: number[]; - nagent: number; - nturn?: number; - nsec?: number; - nplayer?: number; - name?: string; - }); - static restore(data: Board): Board; - toLogJSON(): Board; - getJSON(): { - name: string; - w: number; - h: number; - points: number[]; - nagents: number; - nturn: number; - nsec: number; - nplayer: number; - }; - toJSON(): { - name: string; - width: number; - height: number; - nAgent: number; - nPlayer: number; - nTurn: number; - nSec: number; - points: number[]; - }; + nAgent?: number; + nPlayer?: number; + totalTurn?: number; } declare class Agent { - board: Board; + #private; field: Field; - playerid: number; + playerIdx: number; x: number; y: number; bkx: number; bky: number; - lastaction: Action | null; - constructor(board: Board, field: Field, playerid: number); - static restore(data: Agent, board: Board, field: Field): Agent; - toLogJSON(): Agent & { - board: null; - field: null; - }; - isOnBoard(): boolean; - checkOnBoard(x: number, y: number): boolean; - checkDir(x: number, y: number): boolean; + constructor(field: Field, playeridx: number); + static fromJSON(data: AgentJson, playerIdx: number, field: Field): Agent; + toJSON(): AgentJson; check(act: Action): boolean; - checkPut(x: number, y: number): boolean; - checkNone(_x: number, _y: number): boolean; - checkMove(x: number, y: number): boolean; - checkRemove(x: number, y: number): boolean; - isValidAction(): boolean; + isValidAction(): Action | undefined; putOrMove(): boolean; - put(x: number, y: number): boolean; - none(x: number, y: number): boolean; - move(x: number, y: number): boolean; remove(): boolean; commit(): void; revert(): void; - getJSON(): { - x: number; - y: number; - }; } export declare type ActionType = 1 | 2 | 3 | 4; -declare type ActionRes = 0 | 1 | 2 | 3 | 4 | 5; -export declare type ActionJSON = [number, ActionType, number, number]; +export declare type ActionRes = 0 | 1 | 2 | 3 | 4 | 5; +export declare type ActionArray = [number, ActionType, number, number]; declare class Action { - agentid: number; + agentId: number; type: ActionType; x: number; y: number; @@ -100,102 +49,56 @@ declare class Action { static readonly ERR_ILLEGAL_AGENT = 4; static readonly ERR_ILLEGAL_ACTION = 5; constructor(agentid: number, type: ActionType, x: number, y: number); - static restore(data: Action): Action; - getJSON(): { - agentId: number; - type: ActionType; - x: number; - y: number; - res: ActionRes; - }; + static fromJSON(data: ActionJson): Action; static getMessage(res: ActionRes): string; - static fromJSON: (array: ActionJSON[]) => Action[]; + static fromArray: (array: ActionArray[]) => Action[]; } declare type FieldType = typeof Field.AREA | typeof Field.WALL; -declare type FieldCell = { +export declare type FieldTile = { type: FieldType; player: null | number; }; +export declare type FieldInit = Omit; declare class Field { - board: Board; - field: FieldCell[]; + width: number; + height: number; + nAgent: number; + nPlayer: number; + points: number[]; + tiles: FieldTile[]; static readonly AREA = 0; static readonly WALL = 1; - constructor(board: Board); - static restore(data: ReturnType, board: Board): Field; - toLogJSON(): { - field: { - type: FieldType; - player: number; - }[]; - board: any; - }; + constructor({ width, height, points, nAgent, nPlayer }: FieldInit); + static fromJSON(data: FieldJson): Field; set(x: number, y: number, att: FieldType, playerid: number | null): void; - get(x: number, y: number): FieldCell; + get(x: number, y: number): FieldTile; setAgent(playerid: number, x: number, y: number): boolean; - fillBase(): void; + fillArea(): void; getPoints(): Point[]; - getJSON(): FieldCell[]; } +export declare type GameInit = Board; declare class Game { - board: Board; + #private; + totalTurn: number; players: Player[]; - nturn: number; - nsec: number; - gaming: boolean; - ending: boolean; field: Field; log: { players: { point: Point; - actions: ReturnType[]; + actions: Action[]; }[]; }[]; turn: number; - constructor(board: Board); - static restore(data: Game): Game; - toLogJSON(): Game & { - field: ReturnType; - }; + constructor(gameInit: GameInit); + static fromJSON(data: GameJson): Game; attachPlayer(player: Player): boolean; - isReady(): boolean; + getStatus(): "ended" | "free" | "ready" | "gaming"; isFree(): boolean; + isReady(): boolean; isGaming(): boolean; + isEnded(): boolean; start(): void; nextTurn(): boolean; - checkActions(actions: Action[][]): void; - checkConflict(actions: Action[][]): void; - checkAgentConflict(): void; - putOrMove(): void; - revertOverlap(): void; - removeOrNot(): void; - revertNotOwnerWall(): void; - commit(): void; - getStatusJSON(): { - players: ReturnType[]; - board: ReturnType; - field: ReturnType; - agents: ReturnType[][]; - points: ReturnType; - log: typeof Game.prototype.log; - }; - toJSON(): { - gaming: typeof Game.prototype.gaming; - ending: typeof Game.prototype.ending; - board: ReturnType | null; - turn: typeof Game.prototype.turn; - totalTurn: typeof Game.prototype.nturn; - tiled: typeof Game.prototype.field.field | null; - players: { - id: string; - agents: { - x: number; - y: number; - }[]; - point: ReturnType[0]; - }[]; - log: typeof Game.prototype.log; - }; } declare class Player { id: string; @@ -205,35 +108,11 @@ declare class Player { index: number; agents: Agent[]; constructor(id: string, spec?: string); - static restore(data: Player, game?: Game): Player; - toLogJSON(): Player; + static fromJSON(data: PlayerJson, game?: Game): Player; + toJSON(): PlayerJson; setGame(game: T): void; - noticeStart(): void; setActions(actions: Action[]): typeof Game.prototype.turn; getActions(): typeof Player.prototype.actions; clearActions(): void; - getJSON(): { - userId: typeof Player.prototype.id; - spec: typeof Player.prototype.spec; - index: typeof Player.prototype.index; - }; -} -declare class Kakomimasu { - games: T[]; - boards: Board[]; - constructor(); - appendBoard(board: Board): void; - getBoards(): typeof Kakomimasu.prototype.boards; - /** - * @deprecated use addGame - */ - createGame(...param: ConstructorParameters): Game; - addGame(game: T): T; - getGames(): T[]; - getFreeGames(): T[]; - /** - * @deprecated use Player class - */ - createPlayer(playername: string, spec?: string): Player; } -export { Action, Agent, Board, Field, Game, Kakomimasu, Player }; +export { Action, Agent, Field, Game, Player }; diff --git a/types/json_type.d.ts b/types/json_type.d.ts new file mode 100644 index 0000000..8235455 --- /dev/null +++ b/types/json_type.d.ts @@ -0,0 +1,34 @@ +import type { ActionRes, ActionType, FieldInit, FieldTile, Point } from "./Kakomimasu.js"; +export interface AgentJson { + x: number; + y: number; +} +export interface ActionJson { + agentId: number; + type: ActionType; + x: number; + y: number; + res: ActionRes; +} +export declare type FieldJson = Required & { + tiles: FieldTile[]; +}; +export interface PlayerJson { + id: string; + spec: string; + actions: ActionJson[]; + index: number; + agents: AgentJson[]; +} +export interface GameJson { + turn: number; + totalTurn: number; + field: FieldJson; + players: PlayerJson[]; + log: { + players: { + point: Point; + actions: ActionJson[]; + }[]; + }[]; +} diff --git a/util.js b/util.js deleted file mode 100644 index e2db8e6..0000000 --- a/util.js +++ /dev/null @@ -1,11 +0,0 @@ -const util = {}; - -util.rnd = (n) => { - return Math.floor(Math.random() * n); // MT is better -}; - -util.p = (json) => { - console.log(JSON.stringify(json, null, 2)); -}; - -export default util;