Skip to content

Commit

Permalink
Merge pull request #372 from chesslablab/issue/370-Update-the-ELO-of-…
Browse files Browse the repository at this point in the history
…the-players

Issue/370 update the elo of the players
  • Loading branch information
programarivm authored Sep 12, 2024
2 parents 6a4b535 + 52dde49 commit 1830827
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 109 deletions.
176 changes: 84 additions & 92 deletions composer.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Command/Data/Cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function __construct(Db $db)
$this->commands->attach(new ResultPlayerCommand($db));
$this->commands->attach(new ResultCommand($db));
$this->commands->attach(new SearchCommand($db));
$this->commands->attach(new TotpRefreshCommand($db));
$this->commands->attach(new TotpSignInCommand($db));
$this->commands->attach(new TotpSignUpCommand($db));
}
Expand Down
60 changes: 60 additions & 0 deletions src/Command/Data/TotpRefreshCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace ChessServer\Command\Data;

use ChessServer\Command\AbstractCommand;
use ChessServer\Command\Db;
use ChessServer\Socket\AbstractSocket;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class TotpRefreshCommand extends AbstractCommand
{
public function __construct(Db $db)
{
parent::__construct($db);

$this->name = '/totp_refresh';
$this->description = 'Refresh the TOTP access token.';
$this->params = [
'params' => '<string>',
];
}

public function validate(array $argv)
{
return count($argv) - 1 === count($this->params);
}

public function run(AbstractSocket $socket, array $argv, int $id)
{
$params = json_decode(stripslashes($argv[1]), true);

if (isset($params['access_token'])) {
$decoded = JWT::decode($params['access_token'], new Key($_ENV['JWT_SECRET'], 'HS256'));
$sql = "SELECT * FROM users WHERE username = :username";
$values[] = [
'param' => ":username",
'value' => $decoded->username,
'type' => \PDO::PARAM_STR,
];
$arr = $this->db->query($sql, $values)->fetch(\PDO::FETCH_ASSOC);
$payload = [
'iss' => $_ENV['JWT_ISS'],
'iat' => time(),
'exp' => time() + 3600, // one hour by default
'username' => $arr['username'],
'elo' => $arr['elo'],
];
return $socket->getClientStorage()->sendToOne($id, [
$this->name => [
'access_token' => JWT::encode($payload, $_ENV['JWT_SECRET'], 'HS256'),
],
]);
}

return $socket->getClientStorage()->sendToOne($id, [
$this->name => null,
]);
}
}
4 changes: 3 additions & 1 deletion src/Command/Game/AcceptPlayRequestCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ public function run(AbstractSocket $socket, array $argv, int $id)

if ($gameMode->getStatus() === PlayMode::STATUS_PENDING) {
$decoded = $gameMode->getJwtDecoded();
$decoded->username->{(new Color)->opp($decoded->color)} = $params['username'] ?? self::ANONYMOUS_USER;
$color = (new Color())->opp($decoded->color);
$decoded->username->{$color} = $params['username'] ?? self::ANONYMOUS_USER;
$decoded->elo->{$color} = $params['elo'];
$ids = [...$gameMode->getResourceIds(), $id];
$gameMode->setJwt((array) $decoded)
->setResourceIds($ids)
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Game/Cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function __construct(Db $db)
$this->commands->attach(new PlayLanCommand());
$this->commands->attach(new PlayRavCommand());
$this->commands->attach(new RandomizerCommand());
$this->commands->attach(new RestartCommand());
$this->commands->attach(new RestartCommand($db));
$this->commands->attach(new StartCommand($db));
$this->commands->attach(new StockfishCommand());
$this->commands->attach(new TutorFenCommand());
Expand Down
11 changes: 8 additions & 3 deletions src/Command/Game/Game.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,16 @@ public function setBoard(AbstractBoard $board): Game

public function state(): object
{
$end = $this->end();

return (object) [
'turn' => $this->board->turn,
'movetext' => $this->board->movetext(),
'fen' => $this->board->toFen(),
'end' => $this->end(),
...($end
? ['end' => $end]
: []
),
];
}

Expand Down Expand Up @@ -123,7 +128,7 @@ public function playLan(string $color, string $lan): bool
return $this->board->playLan($color, $lan);
}

protected function end(): array
protected function end(): ?array
{
if ($this->board->doesWin()) {
// TODO ...
Expand Down Expand Up @@ -167,6 +172,6 @@ protected function end(): array
];
}

return [];
return null;
}
}
8 changes: 4 additions & 4 deletions src/Command/Game/Mode/AbstractMode.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,22 @@ public function res($params, $cmd)
$isValid = $this->game->playLan($params['color'], $params['lan']);
return [
$cmd->name => [
... (array) $this->game->state(),
...(array) $this->game->state(),
'variant' => $this->game->getVariant(),
'isValid' => $isValid,
],
];

case StockfishCommand::class:
if (!$this->game->state()->end) {
if (!$this->game->state()->end) {
$computer = $this->game->computer($params['options'], $params['params']);
if ($computer['pgn']) {
$this->game->play($this->game->state()->turn, $computer['pgn']);
}
}
return [
$cmd->name => [
... (array) $this->game->state(),
...(array) $this->game->state(),
'variant' => $this->game->getVariant(),
],
];
Expand All @@ -88,7 +88,7 @@ public function res($params, $cmd)
$this->game->setBoard($board);
return [
$cmd->name => [
... (array) $this->game->state(),
...(array) $this->game->state(),
'variant' => $this->game->getVariant(),
],
];
Expand Down
70 changes: 65 additions & 5 deletions src/Command/Game/Mode/PlayMode.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

namespace ChessServer\Command\Game\Mode;

use Chess\Elo\Game as EloGame;
use Chess\Elo\Player as EloPlayer;
use Chess\Variant\Classical\PGN\AN\Color;
use Chess\Variant\Classical\PGN\AN\Termination;
use ChessServer\Command\Db;
use ChessServer\Command\Game\Game;
use ChessServer\Command\Game\PlayLanCommand;
use Firebase\JWT\JWT;
Expand All @@ -20,7 +24,9 @@ class PlayMode extends AbstractMode

const SUBMODE_ONLINE = 'online';

protected $jwt;
protected string $jwt;

protected Db $db;

protected string $status;

Expand All @@ -30,11 +36,12 @@ class PlayMode extends AbstractMode

protected array $timer;

public function __construct(Game $game, array $resourceIds, string $jwt)
public function __construct(Game $game, array $resourceIds, string $jwt, Db $db)
{
parent::__construct($game, $resourceIds);

$this->jwt = $jwt;
$this->db = $db;
$this->hash = hash('adler32', $jwt);
$this->status = self::STATUS_PENDING;
}
Expand Down Expand Up @@ -120,17 +127,70 @@ protected function updateTimer(string $color)
$this->updatedAt = $now;
}

protected function elo(string $result, int $i, int $j): array
{
$w = new EloPlayer($i);
$b = new EloPlayer($j);
$game = (new EloGame($w, $b))->setK(32);
if ($result === Termination::WHITE_WINS) {
$game->setScore(1, 0);
} elseif ($result === Termination::DRAW) {
$game->setScore(0, 0);
} elseif ($result === Termination::BLACK_WINS) {
$game->setScore(0, 1);
}
$game->count();

return [
Color::W => $w->getRating(),
Color::B => $b->getRating(),
];
}

protected function eloQuery(string $username, int $elo): void
{
$sql = "UPDATE users SET elo = :elo WHERE username = :username";
$values= [
[
'param' => ":username",
'value' => $username,
'type' => \PDO::PARAM_STR,
],
[
'param' => ":elo",
'value' => $elo,
'type' => \PDO::PARAM_INT,
],
];

$this->db->query($sql, $values);
}

public function res($params, $cmd)
{
switch (get_class($cmd)) {
case PlayLanCommand::class:
$isValid = $this->game->playLan($params['color'], $params['lan']);
$this->updateTimer($params['color']);
if ($isValid) {
if (isset($this->game->state()->end)) {
$decoded = $this->getJwtDecoded();
if ($decoded->elo->{Color::W} && $decoded->elo->{Color::B}) {
$elo = $this->elo(
$this->game->state()->end['result'],
$decoded->elo->{Color::W},
$decoded->elo->{Color::B}
);
$this->eloQuery($decoded->username->{Color::W}, $elo[Color::W]);
$this->eloQuery($decoded->username->{Color::B}, $elo[Color::B]);
}
} else {
$this->updateTimer($params['color']);
}
}
return [
$cmd->name => [
... (array) $this->game->state(),
...(array) $this->game->state(),
'variant' => $this->game->getVariant(),
// play mode information
'timer' => $this->timer,
'isValid' => $isValid,
],
Expand Down
6 changes: 5 additions & 1 deletion src/Command/Game/RestartCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
use Chess\Variant\Chess960\FEN\StrToBoard as Chess960FenStrToBoard;
use Chess\Variant\Classical\PGN\AN\Color;
use ChessServer\Command\AbstractCommand;
use ChessServer\Command\Db;
use ChessServer\Command\Game\Game;
use ChessServer\Command\Game\Mode\PlayMode;
use ChessServer\Socket\AbstractSocket;
use Firebase\JWT\JWT;

class RestartCommand extends AbstractCommand
{
public function __construct()
public function __construct(Db $db)
{
parent::__construct($db);

$this->name = '/restart';
$this->description = 'Restarts a game.';
$this->params = [
Expand Down Expand Up @@ -45,6 +48,7 @@ public function run(AbstractSocket $socket, array $argv, int $id)
$game,
$gameMode->getResourceIds(),
JWT::encode((array) $decoded, $_ENV['JWT_SECRET'], 'HS256'),
$this->db
);
$newGameMode->setStatus(PlayMode::STATUS_ACCEPTED)
->setStartedAt(time())
Expand Down
17 changes: 15 additions & 2 deletions src/Command/Game/StartCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class StartCommand extends AbstractCommand
public function __construct(Db $db)
{
parent::__construct($db);

$this->name = '/start';
$this->description = 'Starts a new game.';
$this->params = [
Expand Down Expand Up @@ -138,6 +138,14 @@ public function run(AbstractSocket $socket, array $argv, int $id)
? $params['settings']['username']
: self::ANONYMOUS_USER,
],
'elo' => [
Color::W => $params['settings']['color'] === Color::W && $params['settings']['elo']
? $params['settings']['elo']
: null,
Color::B => $params['settings']['color'] === Color::B && $params['settings']['elo']
? $params['settings']['elo']
: null,
],
'submode' => $params['settings']['submode'],
'color' => $params['settings']['color'],
'min' => $params['settings']['min'],
Expand All @@ -152,7 +160,12 @@ public function run(AbstractSocket $socket, array $argv, int $id)
: []
),
];
$gameMode = new PlayMode($game, [$id], JWT::encode($payload, $_ENV['JWT_SECRET'], 'HS256'));
$gameMode = new PlayMode(
$game,
[$id],
JWT::encode($payload, $_ENV['JWT_SECRET'], 'HS256'),
$this->db
);
$socket->getGameModeStorage()->set($gameMode);
if ($params['settings']['submode'] === PlayMode::SUBMODE_ONLINE) {
$socket->getClientStorage()->sendToAll([
Expand Down

0 comments on commit 1830827

Please sign in to comment.