Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
arssly committed Oct 10, 2024
1 parent ac82671 commit 2794740
Show file tree
Hide file tree
Showing 17 changed files with 444 additions and 148 deletions.
4 changes: 2 additions & 2 deletions apps/web/src/app/@modal/(.)create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const INPUT_FIELDS = [
{ label: "Maximum Players", name: "maxPlayers" },
{ label: "Minimum Buy-in", name: "minBuyIn" },
{ label: "Maximum Buy-in", name: "maxBuyIn" },
{ label: "Waiting Blocks", name: "waitingBlocks" },
{ label: "Waiting timeOut(seconds)", name: "waitingTimeOut" },
];

export default function GameCreateModal() {
Expand Down Expand Up @@ -64,9 +64,9 @@ export default function GameCreateModal() {
formValues.smallBlind,
formValues.numberOfRaises,
formValues.minPlayers,
formValues.maxPlayers,
formValues.minBuyIn,
formValues.maxBuyIn,
formValues.waitingTimeOut,
formValues.chipUnit,
1000,
account!.address,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/games/[id]/state/actions/gameActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export const initGame = async (
setGameEventListeners(finalGame);
const entryGameState = finalGame.gameState;
if (!entryGameState) throw Error("should have existed");
state$.gameState.players.set(entryGameState.seats.map((p) => p));
state$.gameState.players.set(entryGameState.players.map((p) => p));
state$.gameState.status.set(entryGameState.status);
state$.gameState.dealer.set(entryGameState.seats[entryGameState.dealerIndex]!);
state$.gameState.dealer.set(entryGameState.players[entryGameState.dealerIndex]!);
state$.loading.set(false);
//state$.initializing.set(false);
};
Expand Down
1 change: 0 additions & 1 deletion packages/ts-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
"@aptos-labs/ts-sdk": "^1.28.0",
"@jeton/zk-deck": "*",
"events": "^3.3.0",
"graffle": "^0.0.0",
"graphql": "^16.9.0",
"graphql-request": "^7.1.0",
"piesocket-js": "^5.1.0"
Expand Down
147 changes: 116 additions & 31 deletions packages/ts-sdk/src/Jeton/Jeton.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { EventEmitter } from "events";
import type { ZKDeck } from "@jeton/zk-deck";
import {
type OnChainDataSource,
type OnChainDataSourceInstance,
OnChainEventTypes,
type OnChainPlayerCheckedInData,
type OnChainTableObject,
type OnChainShuffledDeckData,
} from "@src/OnChainDataSource";
import { AptosOnChainDataSource } from "@src/OnChainDataSource/AptosOnChainDataSource";
import onChainDataMapper from "@src/OnChainDataSource/onChainDataMapper";
import type {
ChipUnits,
GameEventMap,
import onChainDataMapper, { covertTableInfo } from "@src/OnChainDataSource/onChainDataMapper";
import {
GameEventTypes,
type ChipUnits,
type GameEventMap,
GameStatus,
PlacingBettingActions,
Player,
TableInfo,
type PlacingBettingActions,
type Player,
type TableInfo,
} from "@src/types";
import { createLocalZKDeck } from "@src/utils/createZKDeck";
import { type PendingMemo, createPendingMemo } from "@src/utils/PendingMemo";
import { hexStringToUint8Array } from "@src/utils/unsignedInt";

export type ZkDeckUrls = {
shuffleEncryptDeckWasm: string;
Expand All @@ -33,10 +37,8 @@ export type JetonConfigs = {
secretKey?: bigint;
};

export type Seats = [Player | null];

export interface JGameState {
seats: Seats;
players: Player[];
dealerIndex: number;
status: GameStatus;
}
Expand All @@ -50,6 +52,7 @@ export class Jeton extends EventEmitter<GameEventMap> {
private publicKey: Uint8Array;
public gameState?: JGameState;
public mySeatIndex?: number;
private pendingMemo: PendingMemo;

constructor(config: JetonConfigs) {
super();
Expand All @@ -61,35 +64,46 @@ export class Jeton extends EventEmitter<GameEventMap> {
this.zkDeck = config.zkDeck;
this.secretKey = config.secretKey ?? this.zkDeck.sampleSecretKey();
this.publicKey = this.zkDeck.generatePublicKey(this.secretKey);

this.pendingMemo = createPendingMemo();
}

async checkIn(buyInAmount: number) {
console.log("check in, tableInfo", this.tableInfo);
const rawState = await this.onChainDataSource.queryGameState(this.tableInfo.id, ["seets"]);
const seats = onChainDataMapper.convertSeats(rawState.seets);
const alreadyCheckedIn = this.isAlreadyCheckedIn(seats);
const rawState = await this.onChainDataSource.queryGameState(this.tableInfo.id);
const gameState = onChainDataMapper.convertJetonState(rawState);
const alreadyCheckedIn = this.isAlreadyCheckedIn(gameState.players);
console.log("is already checked in?", alreadyCheckedIn, " game state:", gameState);

if (!alreadyCheckedIn) {
await this.onChainDataSource.checkIn(this.tableInfo.id, buyInAmount, this.publicKey);
}

const rawState2 = await this.onChainDataSource.queryGameState(this.tableInfo.id, [
"seets",
"dealer_index",
"phase",
"time_out",
]);
this.gameState = onChainDataMapper.convertJetonState(rawState2) as JGameState;
console.log("game state", this.gameState);
const updatedRawState = await this.onChainDataSource.queryGameState(this.tableInfo.id);
this.gameState = onChainDataMapper.convertJetonState(updatedRawState);
this.setMySeatIndex();
console.log("set seat index");
console.log("my seat index is", this.mySeatIndex);
this.addOnChainListeners();
this.checkForActions(updatedRawState);
this.createTableEvents(updatedRawState);
}

private createTableEvents(onChainTableObject: OnChainTableObject) {
const shufflingPlayerIndex =
onChainTableObject.state.__variant__ === "Playing" &&
onChainTableObject.state.phase.__variant__ === "Shuffle" &&
onChainTableObject.state.phase.turn_index;
if (shufflingPlayerIndex === false) {
console.log("shuffle ended, lets do private card decryption");
return;
}
const shufflingPlayer = onChainTableObject.roster.players[shufflingPlayerIndex]!;
this.emit(GameEventTypes.PLAYER_SHUFFLING, onChainDataMapper.convertPlayer(shufflingPlayer));
}

private addOnChainListeners() {
this.onChainDataSource.on(OnChainEventTypes.PLAYER_CHECKED_IN, this.newPlayerCheckedIn);
// this.onChainDataSource.on(OnChainEventTypes.GAME_STARTED, this.gameStarted);
// this.onChainDataSource.on(OnChainEventTypes.SHUFFLED_DECK, this.playerShuffledDeck);
this.onChainDataSource.on(OnChainEventTypes.SHUFFLED_DECK, this.playerShuffledDeck);
// this.onChainDataSource.on(
// OnChainEventTypes.PRIVATE_CARDS_SHARES_RECEIVED,
// this.receivedPrivateCardsShares,
Expand All @@ -99,29 +113,99 @@ export class Jeton extends EventEmitter<GameEventMap> {
// this.receivedPublicCardsShares,
// );
// this.onChainDataSource.on(OnChainEventTypes.PLAYER_PLACED_BET, this.receivedPlayerBet);
this.onChainDataSource.listenToTableEvents(this.tableInfo.id);
}

private newPlayerCheckedIn = (data: OnChainPlayerCheckedInData) => {
private newPlayerCheckedIn = async (data: OnChainPlayerCheckedInData) => {
if (!this.gameState) throw new Error("game state must exist");
console.log("new player checked in", data);
const onChainTableObject = await this.pendingMemo.memoize(this.queryGameState);
const newJState = onChainDataMapper.convertJetonState(onChainTableObject);
const newPlayer = newJState.players.find((p) => p.id === data.address);
if (!newPlayer) throw new Error("new player must have existed!!");
// TODO: are you sure? you want to replace all the players?
// this.sendUIevents();
this.emit(GameEventTypes.NEW_PLAYER_CHECK_IN, newPlayer);
// this.takeAction();
if (
newJState.status === GameStatus.Shuffle &&
this.gameState.status === GameStatus.AwaitingStart
) {
const dealerIndex = newJState.dealerIndex;
//@ts-ignore
const shufflingPlayer = onChainTableObject.state?.phase?.turn_index;
this.emit(GameEventTypes.HAND_STARTED, { dealer: newJState.players[dealerIndex]! });
this.emit(GameEventTypes.PLAYER_SHUFFLING, newJState.players[shufflingPlayer]!);
this.checkForActions(onChainTableObject);
}

// this.syncState(newGameState);
this.gameState = newJState;
};

private async playerShuffledDeck(data: OnChainShuffledDeckData) {
const onChainTableObject = await this.pendingMemo.memoize(this.queryGameState);
const shufflingPlayerIndex =
onChainTableObject.state.__variant__ === "Playing" &&
onChainTableObject.state.phase.__variant__ === "Shuffle" &&
onChainTableObject.state.phase.turn_index;
if (shufflingPlayerIndex === false) {
console.log("shuffle ended, lets do private card decryption");
return;
}
const shufflingPlayer = onChainTableObject.roster.players[shufflingPlayerIndex]!;
this.emit(GameEventTypes.PLAYER_SHUFFLING, onChainDataMapper.convertPlayer(shufflingPlayer));
this.checkForActions(onChainTableObject);
}

private checkForActions(onChainTableObject: OnChainTableObject) {
if (
onChainTableObject.state.__variant__ === "Playing" &&
onChainTableObject.state.phase.__variant__ === "Shuffle" &&
onChainTableObject.roster.players[onChainTableObject.state.phase.turn_index]?.addr ===
this.playerId
) {
const publicKeys = onChainTableObject.roster.players.map((p) =>
hexStringToUint8Array(p.public_key),
);
this.shuffle(hexStringToUint8Array(onChainTableObject.state.deck), publicKeys);
}
}

private async shuffle(deck: Uint8Array, publicKeys: Uint8Array[]) {
console.log("going to shuffle deck");
if (!this.zkDeck) throw new Error("zkDeck must have been created by now!");
if (!this.gameState) throw new Error("game state must be present");
const aggregatedPublicKey = this.zkDeck.generateAggregatedPublicKey(publicKeys);
const { proof, outputDeck } = await this.zkDeck.proveShuffleEncryptDeck(
aggregatedPublicKey,
deck,
);
console.log("shuffled deck", outputDeck);
this.onChainDataSource.shuffledDeck(this.tableInfo.id, outputDeck, proof);
}

private setMySeatIndex() {
if (!this.gameState) throw new Error("No game state");
for (const [seat, player] of Object.entries(this.gameState.seats)) {
for (const [index, player] of Object.entries(this.gameState.players)) {
if (player && player.id === this.playerId) {
this.mySeatIndex = Number(seat);
this.mySeatIndex = Number(index);
return;
}
}
}

private isAlreadyCheckedIn(seats: Seats): boolean {
for (const player of seats) {
private isAlreadyCheckedIn(players: Player[]): boolean {
for (const player of players) {
if (player && player.id === this.playerId) return true;
}
return false;
}

private queryGameState = () => {
return this.onChainDataSource.queryGameState(this.tableInfo.id);
};

public placeBet(action: PlacingBettingActions) {
console.log("placeBet Called", action);
}
Expand Down Expand Up @@ -157,7 +241,7 @@ export class Jeton extends EventEmitter<GameEventMap> {
accountAddress,
signAndSubmitTransaction,
);
const tableInfo = await onChainDataSource.createTable(
const [tableAddress, tableObject] = await onChainDataSource.createTable(
smallBlind,
numberOfRaises,
minPlayers,
Expand All @@ -168,6 +252,7 @@ export class Jeton extends EventEmitter<GameEventMap> {
chipUnit,
publicKey,
);
const tableInfo = covertTableInfo(tableAddress, tableObject);

const jeton = new Jeton({
tableInfo,
Expand Down
Loading

0 comments on commit 2794740

Please sign in to comment.