paillier integration sort of
This commit is contained in:
@ -135,14 +135,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
document.querySelectorAll(".claim").forEach((el) =>
|
||||
el.addEventListener("click", (ev) => {
|
||||
let region = ev.target.closest(".node").dataset.name;
|
||||
socket.emit("message", Packet.createRegionClaim(region));
|
||||
game.us.sendClaim(region);
|
||||
})
|
||||
);
|
||||
|
||||
document.querySelectorAll(".reinforce").forEach((el) =>
|
||||
el.addEventListener("click", (ev) => {
|
||||
let region = ev.target.closest(".node").dataset.name;
|
||||
socket.emit("message", Packet.createReinforce(region));
|
||||
game.us.sendReinforce(region);
|
||||
})
|
||||
);
|
||||
|
||||
@ -231,7 +231,7 @@ function showRemainingReinforcements() {
|
||||
if (game.isPregame()) {
|
||||
document.querySelector(
|
||||
"#remaining-reinforcements"
|
||||
).innerHTML = `<span>Remaining placements: ${Region.reinforcementsRemaining()}</span>`;
|
||||
).innerHTML = `<span>Remaining placements: ${game.reinforcementsRemaining()}</span>`;
|
||||
} else if (game.isPlaying()) {
|
||||
document.querySelector(
|
||||
"#remaining-reinforcements"
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Player } from "./player.js";
|
||||
|
||||
// In standard Risk, this is 5
|
||||
const _REINFORCEMENT_MULTIPLIER = 1;
|
||||
|
||||
const WAITING = 0;
|
||||
const PRE_GAME = 1;
|
||||
const PLAYING = 2;
|
||||
@ -9,6 +12,8 @@ export class Game {
|
||||
this.us = null;
|
||||
this.players = {};
|
||||
this.state = WAITING;
|
||||
|
||||
this.allPlaced = false;
|
||||
}
|
||||
|
||||
isWaiting() {
|
||||
@ -91,4 +96,33 @@ export class Game {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
reinforcementsRemaining() {
|
||||
if (this.allPlaced) {
|
||||
return 0;
|
||||
} else {
|
||||
return (
|
||||
_REINFORCEMENT_MULTIPLIER * (10 - this.playerCount()) -
|
||||
this.us.totalStrength
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
allReinforcementsPlaced() {
|
||||
if (this.allPlaced) {
|
||||
return true;
|
||||
} else {
|
||||
let totalStrength = Object.values(this.players).reduce(
|
||||
(counter, player) => counter + player.totalStrength,
|
||||
0
|
||||
);
|
||||
|
||||
this.allPlaced =
|
||||
totalStrength >=
|
||||
this.playerCount() *
|
||||
_REINFORCEMENT_MULTIPLIER *
|
||||
(10 - this.playerCount());
|
||||
return this.allPlaced;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +121,11 @@ document.addEventListener("ACT", async (ev) => {
|
||||
if (game.isWaiting()) {
|
||||
game.setReady(data.author, data.ready);
|
||||
} else {
|
||||
// Throw out our own packets
|
||||
if (data.author === game.us) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.author !== game.currentPlayer().id) {
|
||||
if (data.action === "DEFENSE") {
|
||||
await game.players[data.author].setDefense(data.amount);
|
||||
@ -136,13 +141,13 @@ document.addEventListener("ACT", async (ev) => {
|
||||
// Increment to next player.
|
||||
game.currentPlayer().endTurn();
|
||||
}
|
||||
} else if (!Region.allReinforcementsPlaced()) {
|
||||
} else if (!game.allReinforcementsPlaced()) {
|
||||
if (game.currentPlayer().reinforce(data)) {
|
||||
game.currentPlayer().endTurn();
|
||||
}
|
||||
}
|
||||
|
||||
if (Region.allReinforcementsPlaced()) {
|
||||
if (game.allReinforcementsPlaced()) {
|
||||
game.incrementState();
|
||||
}
|
||||
} else {
|
||||
|
@ -1,10 +1,3 @@
|
||||
import { game } from "./main.js";
|
||||
|
||||
let allPlaced = false;
|
||||
|
||||
// In standard Risk, this is 5
|
||||
const _REINFORCEMENT_MULTIPLIER = 1;
|
||||
|
||||
const REGIONS = {};
|
||||
|
||||
class Continent {
|
||||
@ -15,16 +8,16 @@ class Continent {
|
||||
}
|
||||
|
||||
class Strength {
|
||||
constructor(cyphertext) {
|
||||
this.cyphertext = cyphertext;
|
||||
constructor(cipherText) {
|
||||
this.cipherText = cipherText;
|
||||
this.assumedStrength = null;
|
||||
}
|
||||
|
||||
update(amount) {
|
||||
if (this.cyphertext === null) {
|
||||
this.cyphertext = amount;
|
||||
update(cipherText) {
|
||||
if (this.cipherText === null) {
|
||||
this.cipherText = cipherText;
|
||||
} else {
|
||||
this.cyphertext *= amount;
|
||||
this.cipherText.update(cipherText);
|
||||
}
|
||||
|
||||
this.assumedStrength = null;
|
||||
@ -53,40 +46,6 @@ export class Region {
|
||||
);
|
||||
}
|
||||
|
||||
// todo fix
|
||||
static reinforcementsRemaining() {
|
||||
if (allPlaced) {
|
||||
return 0;
|
||||
} else {
|
||||
let totalStrength = Object.values(REGIONS)
|
||||
.filter((region) => region.owner === game.us)
|
||||
.reduce(
|
||||
(counter, region) => counter + region.strength.assumedStrength,
|
||||
0
|
||||
);
|
||||
|
||||
return _REINFORCEMENT_MULTIPLIER * (10 - game.playerCount()) - totalStrength;
|
||||
}
|
||||
}
|
||||
|
||||
static allReinforcementsPlaced() {
|
||||
if (allPlaced) {
|
||||
return true;
|
||||
} else {
|
||||
let totalStrength = Object.values(REGIONS).reduce(
|
||||
(counter, region) => counter + region.strength.assumedStrength,
|
||||
0
|
||||
);
|
||||
|
||||
allPlaced =
|
||||
totalStrength >=
|
||||
game.playerCount() *
|
||||
_REINFORCEMENT_MULTIPLIER *
|
||||
(10 - game.playerCount());
|
||||
return allPlaced;
|
||||
}
|
||||
}
|
||||
|
||||
static getRegion(name) {
|
||||
return REGIONS[name];
|
||||
}
|
||||
@ -95,25 +54,25 @@ export class Region {
|
||||
return Object.values(REGIONS);
|
||||
}
|
||||
|
||||
claim(player) {
|
||||
claim(player, cipherText) {
|
||||
this.owner = player;
|
||||
this.strength.update(player.paillierPubKey.encrypt(1n));
|
||||
this.strength.update(cipherText);
|
||||
this.strength.assumedStrength = 1;
|
||||
}
|
||||
|
||||
reinforce(amount) {
|
||||
this.strength.update(amount);
|
||||
reinforce(cipherText) {
|
||||
this.strength.update(cipherText);
|
||||
}
|
||||
|
||||
isClaimed() {
|
||||
return this.strength.cyphertext !== null;
|
||||
return this.strength.cipherText !== null;
|
||||
}
|
||||
|
||||
displayStrength() {
|
||||
if (this.owner === null) {
|
||||
return "";
|
||||
} else if (this.owner === game.us) {
|
||||
return window.paillier.privKey.decrypt(this.strength.cyphertext).toString();
|
||||
} else if (!this.strength.cipherText.readOnly) {
|
||||
return this.strength.cipherText.plainText;
|
||||
} else if (this.strength.assumedStrength !== null) {
|
||||
return `${this.strength.assumedStrength}`;
|
||||
} else {
|
||||
|
@ -77,29 +77,18 @@ export class Packet {
|
||||
return this._sign(this._createBase("BARRIER"));
|
||||
}
|
||||
|
||||
static createRegionClaim(region) {
|
||||
static createRegionClaim(region, text) {
|
||||
return this._sign({
|
||||
...this._createBase("ACT"),
|
||||
region: region,
|
||||
cipherText: text,
|
||||
});
|
||||
}
|
||||
|
||||
static createReinforce(region) {
|
||||
// todo cache some of these, possibly by pregenerating cyphertexts in a web worker
|
||||
let regions = {};
|
||||
for (let ourRegion of game.us.getRegions()) {
|
||||
if (ourRegion.name !== region) {
|
||||
regions[ourRegion.name] =
|
||||
"0x" + game.us.paillierPubKey.encrypt(0n).toString(16);
|
||||
} else {
|
||||
regions[ourRegion.name] =
|
||||
"0x" + game.us.paillierPubKey.encrypt(1n).toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
static createReinforce(regionCyphertexts) {
|
||||
return this._sign({
|
||||
...this._createBase("ACT"),
|
||||
regions: regions,
|
||||
regions: regionCyphertexts,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Packet } from "./packet.js";
|
||||
import { socket, game, random } from "./main.js";
|
||||
import { RsaPubKey } from "../crypto/rsa.js";
|
||||
import { PaillierPubKey } from "../crypto/paillier.js";
|
||||
import { PaillierPubKey, ReadOnlyCyphertext } from "../crypto/paillier.js";
|
||||
import { Region } from "./map.js";
|
||||
import { showDefenseDom } from "./dom.js";
|
||||
|
||||
@ -16,9 +16,13 @@ let totalDice = 0;
|
||||
|
||||
export class Player {
|
||||
constructor(id, local, rsa_key, paillier_key) {
|
||||
// Game state
|
||||
this.totalStrength = 0;
|
||||
this.ready = false;
|
||||
|
||||
// Protocol state
|
||||
this.timeout = null;
|
||||
this.id = id;
|
||||
this.ready = false;
|
||||
this.rsaPubKey = RsaPubKey.fromJSON(rsa_key);
|
||||
this.paillierPubKey = PaillierPubKey.fromJSON(paillier_key);
|
||||
this.lastPacket = 0;
|
||||
@ -82,7 +86,13 @@ export class Player {
|
||||
let region = Region.getRegion(data.region);
|
||||
|
||||
if (region.owner === null) {
|
||||
region.claim(this);
|
||||
region.claim(
|
||||
this,
|
||||
new ReadOnlyCyphertext(this.paillierPubKey, data.cipherText)
|
||||
);
|
||||
|
||||
this.totalStrength += 1;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -105,14 +115,48 @@ export class Player {
|
||||
}
|
||||
}
|
||||
|
||||
this.totalStrength += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sendClaim(region) {
|
||||
let cipherText = this.paillierPubKey.encrypt(1n);
|
||||
Region.getRegion(region).claim(this, cipherText);
|
||||
|
||||
socket.emit("message", Packet.createRegionClaim(region, cipherText.toString()));
|
||||
|
||||
this.totalStrength += 1;
|
||||
|
||||
this.endTurn();
|
||||
}
|
||||
|
||||
sendReinforce(region) {
|
||||
let regions = {};
|
||||
for (let ourRegion of this.getRegions()) {
|
||||
let cipherText;
|
||||
if (ourRegion.name !== region) {
|
||||
cipherText = this.paillierPubKey.encrypt(0n);
|
||||
} else {
|
||||
cipherText = this.paillierPubKey.encrypt(1n);
|
||||
}
|
||||
|
||||
regions[ourRegion.name] = cipherText.toString();
|
||||
ourRegion.reinforce(cipherText);
|
||||
}
|
||||
|
||||
socket.emit("message", Packet.createReinforce(regions));
|
||||
|
||||
this.totalStrength += 1;
|
||||
|
||||
this.endTurn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a generic action packet representing a player's move.
|
||||
*
|
||||
* @param data Data received via socket
|
||||
* @returns {boolean} Whether this player's turn has ended or not.
|
||||
* @returns {boolean} Whether this player's turn should now end or not.
|
||||
*/
|
||||
async act(data) {
|
||||
if (this.turnPhase === PHASE_REINFORCE) {
|
||||
|
Reference in New Issue
Block a user