my second commit

This commit is contained in:
Pierre Tellier 2024-04-20 14:07:05 +02:00
parent 547b5de903
commit 2d68715003
11 changed files with 708 additions and 0 deletions

28
.eslintrc.cjs Normal file
View File

@ -0,0 +1,28 @@
module.exports = {
env: {
"browser": true,
"jest": true,
"node": true,
},
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-inferrable-types" : "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "off",
"array-callback-return": "error",
"consistent-return": "error",
"eqeqeq": "error",
"no-eval": "error",
"no-fallthrough": "error",
"no-mixed-spaces-and-tabs": "error",
"no-undef": "error",
"no-unreachable": "error",
"no-var": "error",
"prefer-const": "error",
"semi": "error",
},
root: true,
};

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
dist/*
.parcel-cache
.vscode/*
node_modules
package-lock.json

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
parcel:
npm run parcel src/player.html src/*.ts
uncrash:
pkill -9 node
clean:
rm -rf .parcel-cache dist

7
bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "free-carc-viewer",
"version": "1.0.0",
"description": "",
"scripts": {
"build": "npx parcel build --no-source-maps --public-url '.' src/player.html",
"parcel": "npx parcel src/player.html",
"eslint": "npx eslint -c .eslintrc.cjs src",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://thor.enseirb-matmeca.fr/ruby/repositories/11007"
},
"source": "src/player.html",
"author": "David Renault",
"license": "ISC",
"dependencies": {
"@types/node": "^20.12.5",
"@typescript-eslint/eslint-plugin": "^7.5.0",
"@typescript-eslint/parser": "^7.5.0",
"eslint": "^8.57.0",
"parcel": "^2.12.0",
"typescript": "^5.4.4"
}
}

172
src/Board.ts Normal file
View File

@ -0,0 +1,172 @@
/**
* return the WebBoard information
*/
function getWebBoardInfo() {
const gameGrid = document.getElementById("board");
const topDiv = document.getElementById("line0");
if (!gameGrid || !topDiv)
throw Error("The game grid is not initialized. Can't proceed");
const nbColumn: number = topDiv.childElementCount;
const nbLines: number = gameGrid.childElementCount;
return {gameGrid, topDiv, nbLines, nbColumns: nbColumn};
}
/**
* set the size of each div to have them fill the entire screen
*/
function resizeBoard(staySquare: boolean = true) {
const gameInfo = getWebBoardInfo();
const unit = window.innerHeight > window.innerWidth ? "vw" : "vh";
const maxPercent = Math.min(100 / gameInfo.nbColumns, 100 / gameInfo.nbLines);
for (let i = 0; i < gameInfo.nbLines; i++) {
const line = document.getElementById("line" + i);
if (!line)
throw Error("Error while resizing the board, the line " + i + "does not exist");
line.style.height = staySquare ? maxPercent + unit : 100 / gameInfo.nbLines + "%";
line.style.width = "100%";
}
for (let j = 0; j < gameInfo.nbColumns; j++) {
const tiles = document.getElementsByClassName("tile" + j);
if (!tiles)
throw Error("Can't find the tile");
for (let k = 0; k < tiles.length; k++) {
const elm = tiles[k] as HTMLElement;
elm.style.width = staySquare ? maxPercent + unit : 100 / gameInfo.nbColumns + "%";
elm.style.height = "100%";
}
}
}
/**
* create a div where district could be placed
* @param id
* @param lineMode default size to have a smooth animation
*/
function createDiv(id: number, lineMode: boolean) {
const newDiv = document.createElement("div");
if (lineMode) {
newDiv.style.height = "0";
newDiv.style.width = '100%';
} else {
newDiv.style.height = "100%";
newDiv.style.width = "0";
}
newDiv.className = "gridTile tile" + id;
return newDiv;
}
/**
* create a new line of divs
* @param id the lineID.
* @param nbColumn the number of divs that will be added to the line
*/
function createRow(id: number, nbColumn: number) {
const newLine = document.createElement("div");
newLine.id = "line" + id;
newLine.className = "gridLine";
newLine.style.width = "100%";
newLine.style.height = "0";
for (let i = 0; i < nbColumn; i++) {
newLine.append(createDiv(i, true));
}
return newLine;
}
/**
* Add a new line at the top or
*/
function addRowAbove() {
const gameInfo = getWebBoardInfo();
// move existing row down
for (let i = gameInfo.nbLines - 1; i >= 0; i--) {
const l = document.getElementById("line" + i);
if (!l)
throw Error("The game grid is not initialized. Can't proceed");
l.id = "line" + (i + 1);
}
const newLine = createRow(0, gameInfo.nbColumns);
gameInfo.gameGrid.prepend(newLine);
setTimeout(resizeBoard, 10);
}
function addRowUnder() {
const gameInfo = getWebBoardInfo();
const newLine = createRow(gameInfo.nbLines, gameInfo.nbColumns);
gameInfo.gameGrid.appendChild(newLine);
setTimeout(resizeBoard, 10);
}
/**
* add a column at the right or let
*/
function addColumnLeft() {
const gameInfo = getWebBoardInfo();
for (let j = gameInfo.nbColumns - 1; j >= 0; j--) {
const tiles = document.getElementsByClassName("tile" + j);
if (!tiles)
throw Error("Can't find the tile");
for (let k = 0; k < tiles.length; k++) {
const elm = tiles[k] as HTMLElement;
elm.className = "gridTile tile" + (j + 1);
}
}
for (let i = 0; i < gameInfo.nbLines; i++) {
document.getElementById("line" + i)?.prepend(createDiv(0, false));
}
setTimeout(resizeBoard, 10);
}
function addColumnRight() {
const gameInfo = getWebBoardInfo();
for (let i = 0; i < gameInfo.nbLines; i++) {
document.getElementById("line" + i)?.append(createDiv(gameInfo.nbColumns, false));
}
setTimeout(resizeBoard, 10);
}
/**
* reset the WebBoard
*/
function clearBoard() {
const grid = document.getElementById("gameGrid");
if (!grid)
throw Error("error");
grid.innerHTML = "";
for (let i = 0; i < 6; i++) {
const line = document.createElement("div");
line.id = "line" + i;
line.className = "gridLine";
for (let j = 0; j < 6; j++) {
const tile = document.createElement("div");
tile.className = "gridTile tile" + j;
line.append(tile);
}
grid.append(line);
}
resizeBoard();
}
export {
addColumnRight, addColumnLeft, addRowUnder, addRowAbove, resizeBoard, clearBoard, getWebBoardInfo
};

80
src/index.ts Normal file
View File

@ -0,0 +1,80 @@
import {setGameInfo} from "./moooove";
import {resizeBoard} from "./Board";
type CrashGroup = {
name: string;
passed: number;
status: string;
total: number;
};
type CommandOpts = {
continue: boolean;
record_on: string;
timeout: number;
};
type CrashCommand = {
cause: string;
cmd: string[];
code: number;
key: string[];
opts: CommandOpts;
status: string;
stderr: string;
stdout: string;
};
type CrashReport = {
commands: CrashCommand[];
groups: CrashGroup[];
player_1: string;
player_2: string;
};
type Move = {
qsrc: number;
qdst: number;
adst: number;
};
type Game = {
seed: number;
type: string;
player0: string;
player1: string;
queens0: number[];
queens1: number[];
moves: Move[];
winner?: string;
cause?: string;
crashReport?: CrashReport;
};
let games: Game[] = [];
function main() {
games = JSON.parse(document.getElementById("jsonData")!.textContent!) as Game[];
const crashes = JSON.parse(document.getElementById("errors")!.textContent!) as (CrashReport & { log: Game; })[];
const timeouts: CrashReport[] = [];
crashes.forEach((c) => {
const {log: gameLog, ...crashReportWithoutGame} = c;
if (typeof (gameLog as unknown) === "string") { // Timeout case...
timeouts.push(crashReportWithoutGame);
return;
}
gameLog.crashReport = crashReportWithoutGame;
games.push(gameLog);
});
console.log(games);
console.log(timeouts);
console.log(crashes);
}
window.onload = ((e) => {
main();
setGameInfo();
resizeBoard();
});
export {games};

153
src/moooove.ts Normal file
View File

@ -0,0 +1,153 @@
import {games} from "./index";
import {addColumnLeft, addColumnRight, addRowAbove, addRowUnder} from "./Board";
type Move2 = {
player_id: number;
x: number;
y: number;
}
let min_x = 0;
let max_x = 0;
let min_y = 0;
let max_y = 0;
let gameIndex = 0;
let gameMoveId = 0;
/**
* change the displayed game info to the previous/next game
*/
function switchPrevGame() {
if (gameIndex > 0)
gameIndex--;
setGameInfo();
}
function switchNextGame() {
if (gameIndex < games.length - 1)
gameIndex++;
setGameInfo();
}
/**
* change game info value to gameIndex game
*/
function setGameInfo() {
(<HTMLElement>document.getElementById("seed_disp")).innerText = games[gameIndex].seed + "";
(<HTMLElement>document.getElementById("player0_disp")).innerText = games[gameIndex].player0 + "";
(<HTMLElement>document.getElementById("player1_disp")).innerText = games[gameIndex].player1 + "";
(<HTMLElement>document.getElementById("winner_disp")).innerText = games[gameIndex].winner + "";
(<HTMLElement>document.getElementById("cause_disp")).innerText = games[gameIndex].cause + "";
gameMoveId = 0;
}
function nextMove() {
if (games[gameIndex]["moves"][gameMoveId + 1] !== undefined) {
gameMoveId++;
addMove(games[gameIndex]["moves"][gameMoveId]);
}
}
function prevMove() {
if (gameMoveId > 0) {
removeMove(games[gameIndex]["moves"][gameMoveId]);
gameMoveId--;
}
}
function tolastMove() {
while (games[gameIndex]["moves"][gameMoveId + 1] !== undefined) {
nextMove();
}
}
function toFirstMove() {
while (gameMoveId > 0) {
prevMove();
}
}
/**
* returns if 0 x is in intervall [a, b], a and b included, -1 if x < a, 1 if x > b
* @param x
* @param a
* @param b
*/
function inInterval(x: number, a: number, b: number) {
return x < a ? -1 : x > b ? 1 : 0;
}
/**
* resize the board and add the move to it
* @param move
*/
function addMove(move: Move2) {
switch (inInterval(move.x, min_x, max_x)) {
case 0:
break;
case 1:
max_x++;
addColumnRight();
break;
case -1:
min_x--;
addColumnLeft();
break;
}
switch (inInterval(move.y, min_y, max_y)) {
case 0:
break;
case 1:
max_y++;
addRowUnder();
break;
case -1:
min_y--;
addRowAbove();
break;
}
placeMove(move);
}
/**
* there is the real adding part
* @param move
*/
function placeMove(move: Move2) {
console.log(`real coords: ${move.x - min_x} ${move.y - min_y}`);
const line = <HTMLElement>document.getElementById("line" + (move.y - min_y));
const box = <HTMLElement>line.children[move.x - min_x];
box.style.background = 'darkblue';
box.innerText = gameMoveId + "-> (" + move.x + ", " + move.y + ")";
}
function removeMove(move: Move2) {
console.log(`real coords: ${move.x - min_x} ${move.y - min_y}`);
const line = <HTMLElement>document.getElementById("line" + (move.y - min_y));
const box = <HTMLElement>line.children[move.x - min_x];
box.innerText = "";
box.style.background = "none";
}
const prevGameButton = (<HTMLElement>document.getElementById("prevGameButton"));
const nextGameButton = (<HTMLElement>document.getElementById("nextGameButton"));
const nextMoveButton = (<HTMLElement>document.getElementById("nextMoveButton"));
const prevMoveButton = (<HTMLElement>document.getElementById("prevMoveButton"));
const lastMoveButton = (<HTMLElement>document.getElementById("lastMoveButton"));
const firstMoveButton = (<HTMLElement>document.getElementById("firstMoveButton"));
prevGameButton.addEventListener("click", switchPrevGame);
nextGameButton.addEventListener("click", switchNextGame);
nextMoveButton.addEventListener("click", nextMove);
prevMoveButton.addEventListener("click", prevMove);
lastMoveButton.addEventListener("click", tolastMove);
firstMoveButton.addEventListener("click", toFirstMove);
export {setGameInfo, switchPrevGame};

183
src/player.html Normal file
View File

@ -0,0 +1,183 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!--<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">-->
<link rel="stylesheet" href="../bootstrap.min.css">
<link rel="stylesheet" href="./style.css">
<script src="index.ts" type="module"></script>
<script src="moooove.ts" type="module"></script>
<title>Carcassonne Viewer</title>
</head>
<body>
<div class="container-fluid bg-light">
<h1 id="title">Carcassonne viewer</h1>
<div class="row">
<div class="col-lg-3 sidebar">
<ul class="list-group" id="games_list">
</ul>
</div>
<div class="col-lg-9">
<div id="game_container" class="row">
</div>
<div class="row viewer_controls">
<div class="col-lg-2"></div>
<div class="btn-group col-lg-8" role="group" aria-label="Basic example">
<button type="button" class="btn btn-secondary" id="prevGameButton">|&lt;<&lt;</button>
<button type="button" class="btn btn-secondary" id="firstMoveButton">|&lt;&lt;</button>
<button type="button" class="btn btn-secondary" id="prevMoveButton">&lt;</button>
<button type="button" class="btn btn-secondary" id="nextMoveButton">&gt;</button>
<button type="button" class="btn btn-secondary" id="lastMoveButton">&gt;&gt;|</button>
<button type="button" class="btn btn-secondary" id="nextGameButton">>>>|</button>
</div>
<div class="col-lg-2"></div>
</div>
<table class="table table-striped table-sm" style="text-align: left; width: 100%">
<tbody>
<tr>
<th scope="row" style="width: 15%">Player 0</th>
<td id="player0_disp">player0</td>
</tr>
<tr>
<th scope="row">Player 1</th>
<td id="player1_disp">player1</td>
</tr>
<tr>
<th scope="row">Seed</th>
<td id="seed_disp">uéuéuéu</td>
</tr>
<tr>
<th scope="row">Winner</th>
<td id="winner_disp">winner</td>
</tr>
<tr>
<th scope="row">Cause</th>
<td id="cause_disp">cause</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="games">
<div id="board" class="board">
<div class=gridLine id="line0">
<div class="gridTile tile0"></div>
</div>
</div>
</div>
<div id="jsonData">
<pre>[
{
"seed": 1881916104,
"player0": "Teisipyte",
"player1": "Teisipyte",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": -1, "y": 0 }, { "id": 1, "x": -1, "y": 1 }, { "id": 0, "x": -2, "y": 1 }, { "id": 1, "x": -1, "y": 2 }, { "id": 0, "x": -3, "y": 1 }, { "id": 1, "x": -3, "y": 0 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 2130915275,
"player0": "Hippolyte",
"player1": "Full random player",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": 1, "y": 0 }, { "id": 1, "x": 8, "y": 4 }, { "id": 0, "x": 0, "y": -1 }, { "id": 1, "x": 8, "y": 8 }, { "id": 0, "x": 0, "y": -2 }, { "id": 1, "x": 2, "y": 0 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 2130915275,
"player0": "Full random player",
"player1": "Hippolyte",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": 4, "y": 8 }, { "id": 1, "x": 4, "y": 7 }, { "id": 0, "x": 6, "y": 8 }, { "id": 1, "x": 4, "y": 6 }, { "id": 0, "x": 3, "y": 2 }, { "id": 1, "x": 2, "y": 2 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 2130915275,
"player0": "Hippolyte",
"player1": "Full random player",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": 1, "y": 0 }, { "id": 1, "x": 8, "y": 4 }, { "id": 0, "x": 0, "y": -1 }, { "id": 1, "x": 8, "y": 8 }, { "id": 0, "x": 0, "y": -2 }, { "id": 1, "x": 2, "y": 0 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 2130915275,
"player0": "Full random player",
"player1": "Hippolyte",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": 4, "y": 8 }, { "id": 1, "x": 4, "y": 7 }, { "id": 0, "x": 6, "y": 8 }, { "id": 1, "x": 4, "y": 6 }, { "id": 0, "x": 3, "y": 2 }, { "id": 1, "x": 2, "y": 2 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 3336162595,
"player0": "Androdameia",
"player1": "Phoebe",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": -1, "y": 0 }, { "id": 1, "x": -2, "y": 0 }, { "id": 0, "x": 0, "y": -1 }, { "id": 1, "x": 1, "y": 0 }, { "id": 0, "x": 0, "y": -2 }, { "id": 1, "x": -1, "y": -2 } ],
"winner": "Phoebe",
"cause": "Victory on points for player Phoebe"
}
,
{
"seed": 1127192704,
"player0": "Androdameia",
"player1": "Full random player",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": -1, "y": 0 }, { "id": 1, "x": 9, "y": 5 }, { "id": 0, "x": 9, "y": 6 }, { "id": 1, "x": 0, "y": 7 }, { "id": 0, "x": 9, "y": 7 }, { "id": 1, "x": 3, "y": 0 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 1127192704,
"player0": "Full random player",
"player1": "Androdameia",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": 1, "y": 9 }, { "id": 1, "x": 2, "y": 9 }, { "id": 0, "x": 9, "y": 0 }, { "id": 1, "x": 1, "y": 8 }, { "id": 0, "x": 0, "y": 3 }, { "id": 1, "x": 2, "y": 10 } ],
"winner": "Androdameia",
"cause": "Victory on points for player Androdameia"
}
,
{
"seed": 1127192704,
"player0": "Androdameia",
"player1": "Full random player",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": -1, "y": 0 }, { "id": 1, "x": 9, "y": 5 }, { "id": 0, "x": 9, "y": 6 }, { "id": 1, "x": 0, "y": 7 }, { "id": 0, "x": 9, "y": 7 }, { "id": 1, "x": 3, "y": 0 } ],
"winner": "??",
"cause": "Maximal number of moves reached (7), game is a draw"
}
,
{
"seed": 1127192704,
"player0": "Full random player",
"player1": "Androdameia",
"moves": [ { "id": 4294967295, "x": 0, "y": 0 }, { "id": 0, "x": 1, "y": 9 }, { "id": 1, "x": 2, "y": 9 }, { "id": 0, "x": 9, "y": 0 }, { "id": 1, "x": 1, "y": 8 }, { "id": 0, "x": 0, "y": 3 }, { "id": 1, "x": 2, "y": 10 } ],
"winner": "Androdameia",
"cause": "Victory on points for player Androdameia"
}
]
</pre>
</div>
<div id="error_box" style="display: none">
</div>
<div id="errors" style="background-color: #fee; display: visible">
<pre style="white-space: break-spaces">[
]</pre>
</div>
</body>
</html>

23
src/style.css Normal file
View File

@ -0,0 +1,23 @@
.board {
width: 100vw;
height: 100vh;
background: black;
}
.gridTile {
border: 1px solid white;
color: white;
font-size: 5em;
}
.gridLine {
width: 100%;
height: 200px;
display: flex;
flex-direction: row;
}
.column {
height: 100%;
width: 100%;
}

23
tsconfig.json Normal file
View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "es2020",
"target": "es2020",
"noImplicitAny": true,
"sourceMap": true,
"alwaysStrict": true,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"importHelpers": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"outDir": "dist",
"strictNullChecks": true,
"moduleResolution": "node",
"types": [
"node"
]
},
"include": [
"src/**/*.ts"
]
}