summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--controllers/games/gameId.js21
-rw-r--r--controllers/games/root.js69
-rw-r--r--database/000_init.sql1
-rw-r--r--database/001_games.sql1
-rw-r--r--database/games.js25
-rw-r--r--database/users.js18
-rw-r--r--routes/games.js5
-rw-r--r--routes/root.js10
-rw-r--r--utils/board.js63
-rw-r--r--utils/checks.js15
-rw-r--r--views/game.ejs6
-rw-r--r--views/games.ejs26
12 files changed, 215 insertions, 45 deletions
diff --git a/controllers/games/gameId.js b/controllers/games/gameId.js
index c87a857..d3363fd 100644
--- a/controllers/games/gameId.js
+++ b/controllers/games/gameId.js
@@ -1,17 +1,24 @@
import { getGame } from "../../database/games.js";
-function makePageData(user, cwdata) {
+function makePageData(user, game) {
return {
title: "Jouer",
user: user,
- CWDATA: cwdata,
+ data: game,
};
}
export function gameId_get(req, res) {
- const game = getGame(req.params.gameId);
- // TODO redirect if null
- let cwdata = game; // TODO reformat this object
- console.log(cwdata);
- return res.render("game", makePageData(req.session.user, cwdata));
+ const game = JSON.parse(getGame(req.params.gameId).data);
+ if (game) {
+ if (game.player1.id === req.session.user.id) {
+ game.letters = game.player1.letters;
+ delete game.player2.letters;
+ } else {
+ game.letters = game.player2.letters;
+ delete game.player1.letters;
+ }
+ return res.render("game", makePageData(req.session.user, game));
+ }
+ return res.redirect("/games");
}
diff --git a/controllers/games/root.js b/controllers/games/root.js
index ece7b74..7406815 100644
--- a/controllers/games/root.js
+++ b/controllers/games/root.js
@@ -1,13 +1,66 @@
-import { listGames } from "../../database/games.js";
+import { validationResult } from "express-validator";
+
+import { getUserByUsername } from "../../database/users.js";
+import { listGames, newGame } from "../../database/games.js";
+import { emptyBoard, makeLettersBag, pickLetters, } from "../../utils/board.js";
+
+function makePageData(user) {
+ return {
+ title: "Parties",
+ user: user,
+ games: listGames(user.id),
+ formdata: {
+ name: "",
+ username: "",
+ },
+ errors: {},
+ };
+}
export function root_get(req, res) {
- const data = {
- title: "Liste des parties",
- user: req.session.user,
- games: listGames(req.session.user.id),
+ let page = makePageData(req.session.user);
+ for (let i=0; i<page.games.length; i++) {
+ page.games[i].data = JSON.parse(page.games[i].data);
+ }
+ return res.render("games", page);
+}
+
+function makeNewGameData(name, player1, player2) {
+ let bag = makeLettersBag();
+ return {
+ board: emptyBoard,
+ name: name,
+ player1: {
+ id: player1.id,
+ username: player1.username,
+ score: 0,
+ letters: pickLetters(bag, 7),
+ },
+ player2: {
+ id: player2.id,
+ username: player2.username,
+ score: 0,
+ letters: pickLetters(bag, 7),
+ },
};
- for (let i=0; i<data.games.length; i++) {
- data.games[i].data = JSON.parse(data.games[i].data);
+}
+
+export function root_post(req, res) {
+ let page = makePageData(req.session.user);
+ page.formdata = req.body;
+ page.errors = validationResult(req).mapped();
+ if (Object.keys(page.errors).length === 0) {
+ const player2 = getUserByUsername(page.formdata.username);
+ if (player2) {
+ const gameId = newGame(req.session.user.id, player2.id, makeNewGameData(page.formdata.name, req.session.user, player2));
+ if (gameId) {
+ return res.redirect(302, `/games/${gameId}`);
+ } else {
+ page.errors.mismatch = "Erreur du serveur: la création de partie a échoué";
+ }
+ } else {
+ page.errors.username = { msg: "L'identifiant n'existe pas." };
+ }
}
- return res.render("games", data);
+ return res.render("games", page);
}
diff --git a/database/000_init.sql b/database/000_init.sql
index b9f4f23..e4e180c 100644
--- a/database/000_init.sql
+++ b/database/000_init.sql
@@ -8,3 +8,4 @@ CREATE TABLE users (
email TEXT,
created_at DATE DEFAULT (datetime('now'))
);
+-- TODO deleted column
diff --git a/database/001_games.sql b/database/001_games.sql
index be45084..a500aa6 100644
--- a/database/001_games.sql
+++ b/database/001_games.sql
@@ -10,3 +10,4 @@ CREATE TABLE games (
);
CREATE INDEX idx_games_player1 ON games(player1);
CREATE INDEX idx_games_player2 ON games(player2);
+CREATE INDEX idx_games_last_move_at ON games(last_move_at);
diff --git a/database/games.js b/database/games.js
index b378eb2..dae3ce6 100644
--- a/database/games.js
+++ b/database/games.js
@@ -1,21 +1,14 @@
import db from "./db.js";
-const createGameStatement = db.prepare("INSERT INTO games (player1, player2, data) VALUES (?, ?, ?);");
const getGameStatement = db.prepare("SELECT * from games where id = ?;");
const listGamesStatement = db.prepare("SELECT * from games where player1 = ?1 OR player2 = ?1 ORDER BY last_move_at;");
-
-export function createGame(player1, player2, data) {
- try {
- return createGameStatement.run(player1, player2, data).lastInsertRowId;
- } catch {
- return null;
- }
-}
+const newGameStatement = db.prepare("INSERT INTO games (player1, player2, data) VALUES (?, ?, ?);");
export function getGame(id) {
try {
return getGameStatement.get(id);
- } catch {
+ } catch (err) {
+ console.log(err);
return null;
}
}
@@ -23,7 +16,17 @@ export function getGame(id) {
export function listGames(userId) {
try {
return listGamesStatement.all({ 1: userId });
- } catch {
+ } catch (err) {
+ console.log(err);
return [];
}
}
+
+export function newGame(player1, player2, data) {
+ try {
+ return newGameStatement.run(player1, player2, JSON.stringify(data)).lastInsertRowid;
+ } catch (err) {
+ console.log(err);
+ return null;
+ }
+}
diff --git a/database/users.js b/database/users.js
index 4b534b8..b24e3b4 100644
--- a/database/users.js
+++ b/database/users.js
@@ -5,13 +5,24 @@ import db from "./db.js";
const saltRounds = 10;
const createUserStatement = db.prepare("INSERT INTO users (username, hash, email) VALUES (?, ?, ?);");
-const loginStatement = db.prepare("SELECT id, hash, email FROM users WHERE username = ?;");
+const getUserByUsernameStatement = db.prepare("SELECT id, username, email from users WHERE username = ?;");
+const loginStatement = db.prepare("SELECT id, username, hash, email FROM users WHERE username = ?;");
export async function createUser(username, password, email) {
const hash = await bcrypt.hash(password, saltRounds);
try {
return createUserStatement.run(username, hash, email).lastInsertRowid;
- } catch {
+ } catch (err) {
+ console.log(err);
+ return null;
+ }
+}
+
+export function getUserByUsername(username) {
+ try {
+ return getUserByUsernameStatement.get(username);
+ } catch (err) {
+ console.log(err);
return null;
}
}
@@ -19,7 +30,8 @@ export async function createUser(username, password, email) {
export async function login(username, password) {
try {
var user = loginStatement.get(username);
- } catch {
+ } catch (err) {
+ console.log(err);
return null;
}
const result = await bcrypt.compare(password, user.hash);
diff --git a/routes/games.js b/routes/games.js
index 67d105d..3a27cdb 100644
--- a/routes/games.js
+++ b/routes/games.js
@@ -1,15 +1,18 @@
import express from "express";
import { gameId_get } from "../controllers/games/gameId.js";
-import { root_get } from "../controllers/games/root.js";
+import { root_get, root_post } from "../controllers/games/root.js";
+import bodyParser from "../middlewares/formParser.js";
import requireAuth from "../middlewares/requireAuth.js";
import session from "../middlewares/sessions.js";
+import { checkName, checkUsername } from "../utils/checks.js";
const router = express.Router();
router.use(session);
router.use(requireAuth);
router.get("/", root_get);
+router.post("/", [bodyParser, checkName, checkUsername], root_post);
router.get("/:gameId(\\d+)", gameId_get);
export default router;
diff --git a/routes/root.js b/routes/root.js
index c41f3e1..863b99d 100644
--- a/routes/root.js
+++ b/routes/root.js
@@ -1,23 +1,15 @@
import express from "express";
-import { check } from "express-validator";
import { login_get, login_post } from "../controllers/root/login.js";
import { logout_get } from "../controllers/root/logout.js";
import { root_get } from "../controllers/root/root.js";
import bodyParser from "../middlewares/formParser.js";
import session from "../middlewares/sessions.js";
+import { checkUsername, checkPassword } from "../utils/checks.js";
const router = express.Router();
router.use(session);
-const checkUsername = check("username")
- .trim()
- .matches(/^[a-z][-a-z0-9_]+$/i)
- .withMessage("Un identifiant d'au moins deux charactères est requis.");
-const checkPassword = check("password")
- .isStrongPassword()
- .withMessage("Veuillez utiliser un mot de passe d'au moins 8 caractères contenant au moins une minuscule, majuscule, chiffre et charactère spécial.");
-
router.get("/", root_get);
router.get("/login", login_get);
router.post("/login", [bodyParser, checkUsername, checkPassword], login_post);
diff --git a/utils/board.js b/utils/board.js
new file mode 100644
index 0000000..ba43c45
--- /dev/null
+++ b/utils/board.js
@@ -0,0 +1,63 @@
+export const emptyBoard = [
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ],
+ [ "", "", "", "", "","", "", "", "", "", "", "", "", "", "" ]
+];
+
+export const letters_total = 102;
+
+export function makeLettersBag () {
+ return {
+ letters : {
+ JOKER:{count:2, points:0 },
+ E:{count:15, points:1}, A:{count:9, points:1}, I:{count:9, points:1}, N:{count:6, points:1}, O:{count:6, points:1}, R:{count:6, points:1}, S:{count:6, points:1}, T:{count:6, points:1}, U:{count:6, points:1}, L:{count:5, points:1},
+ D:{count:3, points:2}, G:{count:2, points:2}, M:{count:3, points:2},
+ B:{count:2, points:3}, C:{count:2, points:3}, P:{count:2, points:3},
+ F:{count:2, points:4}, H:{count:2, points:4}, V:{count:2, points:4},
+ J:{count:1, points:8}, Q:{count:1, points:8},
+ K:{count:1, points:10}, W:{count:1, points:10}, X:{count:1, points:10}, Y:{count:1, points:10}, Z:{count:1, points:10},
+ },
+ remaining: letters_total,
+ };
+}
+
+const allLetters = Object.keys(makeLettersBag().letters);
+
+export function pickLetters(bag, count) {
+ if (count > bag.remaining) {
+ count = bag.remaining;
+ }
+ let ret = [];
+ for (let i=0; i<count; i++) {
+ let n = Math.floor(Math.random() * bag.remaining) -1;
+ console.log(n);
+ let j = 0;
+ for (;;) {
+ if (bag.letters[allLetters[j]].count === 0) {
+ j++;
+ } else if (bag.letters[allLetters[j]].count < n) {
+ n -= bag.letters[allLetters[j]].count;
+ j++;
+ } else {
+ n = 0;
+ break;
+ }
+ }
+ console.log("j:", j);
+ bag.letters[allLetters[j]].count--;
+ ret.push(allLetters[j]);
+ }
+ return ret;
+}
diff --git a/utils/checks.js b/utils/checks.js
new file mode 100644
index 0000000..85c51df
--- /dev/null
+++ b/utils/checks.js
@@ -0,0 +1,15 @@
+import { check } from "express-validator";
+
+export const checkName = check("name")
+ .trim()
+ .matches(/^[a-z][-a-z0-9_]+$/i)
+ .withMessage("Un identifiant d'au moins deux charactères est requis.");
+
+export const checkPassword = check("password")
+ .isStrongPassword()
+ .withMessage("Veuillez utiliser un mot de passe d'au moins 8 caractères contenant au moins une minuscule, majuscule, chiffre et charactère spécial.");
+
+export const checkUsername = check("username")
+ .trim()
+ .matches(/^[a-z][-a-z0-9_]+$/i)
+ .withMessage("Un identifiant d'au moins deux charactères est requis.");
diff --git a/views/game.ejs b/views/game.ejs
index e1fab41..7fae405 100644
--- a/views/game.ejs
+++ b/views/game.ejs
@@ -27,10 +27,10 @@
</aside>
</p>
<p>
-<span id="player_1_name">player one name</span>: <span class="player_name" id="player_1_points">0</span><br>
-<span id="player_2_name">player two name</span>: <span class="player_name" id="player_2_points">0</span>
+<span id="player_1_name"><%- data.player1.username %></span>: <span class="player_name" id="player_1_points"><%- data.player1.score %></span><br>
+<span id="player_2_name"><%- data.player2.username %></span>: <span class="player_name" id="player_2_points"><%- data.player2.score %></span>
</p>
<p id="remaining_letters"></p>
-<script>var CWDATA = <%- CWDATA.data %></script>
+<script>var CWDATA = <%- JSON.stringify(data) %></script>
<script type="module" src="/static/index.js"></script>
<%- include("footer") %>
diff --git a/views/games.ejs b/views/games.ejs
index 3189be8..9cee7bb 100644
--- a/views/games.ejs
+++ b/views/games.ejs
@@ -7,11 +7,31 @@
<tr><th>Partie</th><th>Joueur 1</th><th>Joueur 2</th></tr>
<% games.forEach((game) => { %>
<tr>
- <td><a href="/games/<%= game.id %>"><%= game.data.title %></a></td>
- <td><a href="/users/<%= game.player1 %>"><%= game.data.player1.name %></a></td>
- <td><a href="/users/<%= game.player2 %>"><%= game.data.player2.name %></a></td>
+ <td><a href="/games/<%= game.id %>"><%= game.data.name %></a></td>
+ <td><a href="/users/<%= game.player1.id %>"><%= game.data.player1.username %></a></td>
+ <td><a href="/users/<%= game.player2.id %>"><%= game.data.player2.username %></a></td>
</tr>
<% }) %>
</table>
<% } %>
+<% if (Object.keys(errors).length === 0) { %>
+<h2>Nouvelle partie</h2>
+<% } else { %>
+<h2>Oops, erreur lors de la création d'une nouvelle partie!</h2>
+<% } %>
+<form action="/games" method="post">
+ <div class="form-field<%= errors.username ? ' form-field-invalid' : '' %>">
+ <input type="text" placeholder="Identifiant de votre adversaire" name="username" value="<%= formdata.username %>" required autofocus>
+ <% if (errors.username) { %>
+ <label class="error" for="username"><%= errors.username.msg %></label>
+ <% } %>
+ </div>
+ <div class="form-field<%= errors.name ? ' form-field-invalid' : '' %>">
+ <input type="text" placeholder="Nom ou titre de cette partie" name="name" value="<%= formdata.name %>" required>
+ <% if (errors.name) { %>
+ <label class="error" for="name"><%= errors.name.msg %></label>
+ <% } %>
+ </div>
+ <button type="submit">Démarrer</button>
+</form>
<%- include("footer") %>