-
Notifications
You must be signed in to change notification settings - Fork 291
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolves #1463
- nightly
- dev-net
- chore-docs-preview-tag
- barretenberg-v0.69.1
- barretenberg-v0.69.0
- barretenberg-v0.68.2
- barretenberg-v0.68.1
- barretenberg-v0.68.0
- barretenberg-v0.67.1
- barretenberg-v0.67.0
- barretenberg-v0.66.0
- barretenberg-v0.65.2
- barretenberg-v0.65.1
- barretenberg-v0.65.0
- barretenberg-v0.64.0
- barretenberg-v0.63.1
- barretenberg-v0.63.0
- barretenberg-v0.62.0
- barretenberg-v0.61.0
- barretenberg-v0.60.0
- barretenberg-v0.59.0
- barretenberg-v0.58.0
- barretenberg-v0.57.0
- barretenberg-v0.56.0
- barretenberg-v0.55.1
- barretenberg-v0.55.0
- barretenberg-v0.54.0
- barretenberg-v0.53.0
- barretenberg-v0.52.0
- barretenberg-v0.51.1
- barretenberg-v0.51.0
- barretenberg-v0.50.1
- barretenberg-v0.50.0
- barretenberg-v0.49.2
- barretenberg-v0.49.1
- barretenberg-v0.49.0
- barretenberg-v0.48.0
- barretenberg-v0.47.1
- barretenberg-v0.47.0
- barretenberg-v0.46.7
- barretenberg-v0.46.6
- barretenberg-v0.46.5
- barretenberg-v0.46.4
- barretenberg-v0.46.3
- barretenberg-v0.46.2
- barretenberg-v0.46.1
- barretenberg-v0.46.0
- barretenberg-v0.45.1
- barretenberg-v0.45.0
- barretenberg-v0.44.0
- barretenberg-v0.43.0
- barretenberg-v0.42.0
- barretenberg-v0.41.0
- barretenberg-v0.40.1
- barretenberg-v0.40.0
- barretenberg-v0.39.0
- barretenberg-v0.38.0
- barretenberg-v0.37.0
- barretenberg-v0.36.0
- barretenberg-v0.35.1
- barretenberg-v0.35.0
- barretenberg-v0.34.0
- barretenberg-v0.33.0
- barretenberg-v0.32.1
- barretenberg-v0.32.0
- barretenberg-v0.31.0
- barretenberg-v0.30.1
- barretenberg-v0.30.0
- barretenberg-v0.29.0
- barretenberg-v0.28.1
- barretenberg-v0.28.0
- barretenberg-v0.27.2
- barretenberg-v0.27.1
- barretenberg-v0.27.0
- barretenberg-v0.26.6
- barretenberg-v0.26.5
- barretenberg-v0.26.4
- barretenberg-v0.26.3
- barretenberg-v0.26.2
- barretenberg-v0.26.1
- barretenberg-v0.26.0
- barretenberg-v0.25.0
- barretenberg-v0.24.0
- barretenberg-v0.23.0
- barretenberg-v0.22.0
- barretenberg-v0.21.0
- barretenberg-v0.20.0
- barretenberg-v0.19.0
- barretenberg-v0.18.0
- barretenberg-v0.17.0
- barretenberg-v0.16.9
- barretenberg-v0.16.8
- barretenberg-v0.16.7
- barretenberg-v0.16.6
- barretenberg-v0.16.5
- barretenberg-v0.16.4
- barretenberg-v0.16.3
- barretenberg-v0.16.2
- barretenberg-v0.16.1
- barretenberg-v0.16.0
- barretenberg-v0.15.1
- barretenberg-v0.15.0
- barretenberg-v0.14.2
- barretenberg-v0.14.1
- barretenberg-v0.14.0
- barretenberg-v0.13.1
- barretenberg-v0.13.0
- barretenberg-v0.12.0
- barretenberg-v0.11.1
- barretenberg-v0.11.0
- barretenberg-v0.10.1
- barretenberg-v0.10.0
- barretenberg-v0.9.0
- barretenberg-v0.8.14
- barretenberg-v0.8.13
- barretenberg-v0.8.12
- barretenberg-v0.8.11
- barretenberg-v0.8.10
- barretenberg-v0.8.9
- barretenberg-v0.8.8
- barretenberg-v0.8.7
- barretenberg-v0.8.6
- barretenberg-v0.8.5
- barretenberg-v0.8.4
- barretenberg-v0.8.3
- barretenberg-v0.8.2
- barretenberg-v0.8.1
- barretenberg-v0.8.0
- barretenberg-v0.7.10
- barretenberg-v0.7.9
- barretenberg-v0.7.8
- barretenberg-v0.7.7
- barretenberg-v0.7.6
- barretenberg-v0.7.5
- barretenberg-v0.7.4
- barretenberg-v0.7.3
- barretenberg-v0.7.2
- barretenberg-v0.7.1
- barretenberg-v0.7.0
- barretenberg-v0.6.7
- barretenberg-v0.6.6
- barretenberg-v0.6.5
- barretenberg-v0.6.4
- barretenberg-v0.6.3
- barretenberg-v0.6.2
- barretenberg-v0.6.1
- barretenberg.js-v0.69.1
- barretenberg.js-v0.69.0
- barretenberg.js-v0.68.2
- barretenberg.js-v0.68.1
- barretenberg.js-v0.68.0
- barretenberg.js-v0.67.1
- barretenberg.js-v0.67.0
- barretenberg.js-v0.66.0
- barretenberg.js-v0.65.2
- barretenberg.js-v0.65.1
- barretenberg.js-v0.65.0
- barretenberg.js-v0.64.0
- barretenberg.js-v0.63.1
- barretenberg.js-v0.63.0
- barretenberg.js-v0.62.0
- barretenberg.js-v0.61.0
- barretenberg.js-v0.60.0
- barretenberg.js-v0.59.0
- barretenberg.js-v0.58.0
- barretenberg.js-v0.57.0
- barretenberg.js-v0.56.0
- barretenberg.js-v0.55.1
- barretenberg.js-v0.55.0
- barretenberg.js-v0.54.0
- barretenberg.js-v0.53.0
- barretenberg.js-v0.52.0
- barretenberg.js-v0.51.1
- barretenberg.js-v0.51.0
- barretenberg.js-v0.50.1
- barretenberg.js-v0.50.0
- barretenberg.js-v0.49.2
- barretenberg.js-v0.49.1
- barretenberg.js-v0.49.0
- barretenberg.js-v0.48.0
- barretenberg.js-v0.47.1
- barretenberg.js-v0.47.0
- barretenberg.js-v0.46.7
- barretenberg.js-v0.46.6
- barretenberg.js-v0.46.5
- barretenberg.js-v0.46.4
- barretenberg.js-v0.46.3
- barretenberg.js-v0.46.2
- barretenberg.js-v0.46.1
- barretenberg.js-v0.46.0
- barretenberg.js-v0.45.1
- barretenberg.js-v0.45.0
- barretenberg.js-v0.44.0
- barretenberg.js-v0.43.0
- barretenberg.js-v0.42.0
- barretenberg.js-v0.41.0
- barretenberg.js-v0.40.1
- barretenberg.js-v0.40.0
- barretenberg.js-v0.39.0
- barretenberg.js-v0.38.0
- barretenberg.js-v0.37.0
- barretenberg.js-v0.36.0
- barretenberg.js-v0.35.1
- barretenberg.js-v0.35.0
- barretenberg.js-v0.34.0
- barretenberg.js-v0.33.0
- barretenberg.js-v0.32.1
- barretenberg.js-v0.32.0
- barretenberg.js-v0.31.0
- barretenberg.js-v0.30.1
- barretenberg.js-v0.30.0
- barretenberg.js-v0.29.0
- barretenberg.js-v0.28.1
- barretenberg.js-v0.28.0
- barretenberg.js-v0.27.2
- barretenberg.js-v0.27.1
- barretenberg.js-v0.27.0
- barretenberg.js-v0.26.6
- barretenberg.js-v0.26.5
- barretenberg.js-v0.26.4
- barretenberg.js-v0.26.3
- barretenberg.js-v0.26.2
- barretenberg.js-v0.26.1
- barretenberg.js-v0.26.0
- barretenberg.js-v0.25.0
- barretenberg.js-v0.24.0
- barretenberg.js-v0.23.0
- barretenberg.js-v0.22.0
- barretenberg.js-v0.21.0
- barretenberg.js-v0.20.0
- barretenberg.js-v0.19.0
- barretenberg.js-v0.18.0
- barretenberg.js-v0.17.0
- barretenberg.js-v0.16.9
- barretenberg.js-v0.16.8
- barretenberg.js-v0.16.7
- barretenberg.js-v0.16.6
- barretenberg.js-v0.16.5
- barretenberg.js-v0.16.4
- barretenberg.js-v0.16.3
- barretenberg.js-v0.16.2
- barretenberg.js-v0.16.1
- barretenberg.js-v0.16.0
- barretenberg.js-v0.15.1
- barretenberg.js-v0.15.0
- barretenberg.js-v0.14.2
- barretenberg.js-v0.14.1
- barretenberg.js-v0.14.0
- barretenberg.js-v0.13.1
- barretenberg.js-v0.13.0
- barretenberg.js-v0.12.0
- barretenberg.js-v0.11.1
- barretenberg.js-v0.11.0
- barretenberg.js-v0.10.1
- barretenberg.js-v0.10.0
- barretenberg.js-v0.9.0
- barretenberg.js-v0.8.14
- barretenberg.js-v0.8.13
- barretenberg.js-v0.8.12
- barretenberg.js-v0.8.11
- barretenberg.js-v0.8.10
- barretenberg.js-v0.8.9
- barretenberg.js-v0.8.8
- barretenberg.js-v0.8.7
- barretenberg.js-v0.8.6
- barretenberg.js-v0.8.5
- barretenberg.js-v0.8.4
- barretenberg.js-v0.8.3
- barretenberg.js-v0.8.2
- barretenberg.js-v0.8.1
- barretenberg.js-v0.8.0
- barretenberg.js-v0.7.10
- barretenberg.js-v0.7.9
- barretenberg.js-v0.7.8
- barretenberg.js-v0.7.7
- barretenberg.js-v0.7.6
- barretenberg.js-v0.7.5
- barretenberg.js-v0.7.4
- barretenberg.js-v0.7.3
- barretenberg.js-v0.7.2
- barretenberg.js-v0.7.1
- barretenberg.js-v0.7.0
- barretenberg.js-v0.6.7
- barretenberg.js-v0.6.6
- barretenberg.js-v0.6.5
- barretenberg.js-v0.6.4
- barretenberg.js-v0.6.3
- barretenberg.js-v0.6.2
- barretenberg.js-v0.6.1
- backup-4096-2024-03-01
- aztec-packages-v0.69.1
- aztec-packages-v0.69.0
- aztec-packages-v0.68.2
- aztec-packages-v0.68.1
- aztec-packages-v0.68.0
- aztec-packages-v0.67.1
- aztec-packages-v0.67.0
- aztec-packages-v0.66.0
- aztec-packages-v0.65.2
- aztec-packages-v0.65.1
- aztec-packages-v0.65.0
- aztec-packages-v0.64.0
- aztec-packages-v0.63.1
- aztec-packages-v0.63.0
- aztec-packages-v0.62.0
- aztec-packages-v0.61.0
- aztec-packages-v0.60.0
- aztec-packages-v0.59.0
- aztec-packages-v0.58.0
- aztec-packages-v0.57.0
- aztec-packages-v0.56.0
- aztec-packages-v0.55.1
- aztec-packages-v0.55.0
- aztec-packages-v0.54.0
- aztec-packages-v0.53.0
- aztec-packages-v0.52.0
- aztec-packages-v0.51.1
- aztec-packages-v0.51.0
- aztec-packages-v0.50.1
- aztec-packages-v0.50.0
- aztec-packages-v0.49.2
- aztec-packages-v0.49.1
- aztec-packages-v0.49.0
- aztec-packages-v0.48.0
- aztec-packages-v0.47.1
- aztec-packages-v0.47.0
- aztec-packages-v0.46.7
- aztec-packages-v0.46.6
- aztec-packages-v0.46.5
- aztec-packages-v0.46.4
- aztec-packages-v0.46.3
- aztec-packages-v0.46.2
- aztec-packages-v0.46.1
- aztec-packages-v0.46.0
- aztec-packages-v0.45.1
- aztec-packages-v0.45.0
- aztec-packages-v0.44.0
- aztec-packages-v0.43.0
- aztec-packages-v0.42.0
- aztec-packages-v0.41.0
- aztec-packages-v0.40.1
- aztec-packages-v0.40.0
- aztec-packages-v0.39.0
- aztec-packages-v0.38.0
- aztec-packages-v0.37.0
- aztec-packages-v0.36.0
- aztec-packages-v0.35.1
- aztec-packages-v0.35.0
- aztec-packages-v0.34.0
- aztec-packages-v0.33.0
- aztec-packages-v0.32.1
- aztec-packages-v0.32.0
- aztec-packages-v0.31.0
- aztec-packages-v0.30.1
- aztec-packages-v0.30.0
- aztec-packages-v0.29.0
- aztec-packages-v0.28.1
- aztec-packages-v0.28.0
- aztec-packages-v0.27.2
- aztec-packages-v0.27.1
- aztec-packages-v0.27.0
- aztec-packages-v0.26.6
- aztec-packages-v0.26.5
- aztec-packages-v0.26.4
- aztec-packages-v0.26.3
- aztec-packages-v0.26.2
- aztec-packages-v0.26.1
- aztec-packages-v0.26.0
- aztec-packages-v0.25.0
- aztec-packages-v0.24.0
- aztec-packages-v0.23.0
- aztec-packages-v0.22.0
- aztec-packages-v0.21.0
- aztec-packages-v0.20.0
- aztec-packages-v0.19.0
- aztec-packages-v0.18.0
- aztec-packages-v0.17.0
- aztec-packages-v0.16.9
- aztec-packages-v0.16.8
- aztec-packages-v0.16.7
- aztec-packages-v0.16.6
- aztec-packages-v0.16.5
- aztec-packages-v0.16.4
- aztec-packages-v0.16.3
- aztec-packages-v0.16.2
- aztec-packages-v0.16.1
- aztec-packages-v0.16.0
- aztec-packages-v0.15.1
- aztec-packages-v0.15.0
- aztec-packages-v0.14.2
- aztec-packages-v0.14.1
- aztec-packages-v0.14.0
- aztec-packages-v0.13.1
- aztec-packages-v0.13.0
- aztec-packages-v0.12.0
- aztec-packages-v0.11.1
- aztec-packages-v0.11.0
- aztec-packages-v0.10.1
- aztec-packages-v0.10.0
- aztec-packages-v0.9.0
- aztec-packages-v0.8.14
- aztec-packages-v0.8.13
- aztec-packages-v0.8.12
- aztec-packages-v0.8.11
- aztec-packages-v0.8.10
- aztec-packages-v0.8.9
- aztec-packages-v0.8.8
- aztec-packages-v0.8.7
- aztec-packages-v0.8.6
- aztec-packages-v0.8.5
- aztec-packages-v0.8.4
- aztec-packages-v0.8.3
- aztec-packages-v0.8.2
- aztec-packages-v0.8.1
- aztec-packages-v0.8.0
- aztec-packages-v0.7.10
- aztec-packages-v0.7.9
- aztec-packages-v0.7.8
- aztec-packages-v0.7.7
- aztec-packages-v0.7.6
- aztec-packages-v0.7.5
- aztec-packages-v0.7.4
- aztec-packages-v0.7.3
- aztec-packages-v0.7.2
- aztec-packages-v0.7.1
- aztec-packages-v0.7.0
- aztec-packages-v0.6.7
- aztec-packages-v0.6.6
- aztec-packages-v0.6.5
- aztec-packages-v0.6.4
- aztec-packages-v0.6.3
- aztec-packages-v0.6.2
- aztec-packages-v0.6.1
- aztec-package-v0.69.1
- aztec-package-v0.69.0
- aztec-package-v0.68.2
- aztec-package-v0.68.1
- aztec-package-v0.68.0
- aztec-package-v0.67.1
- aztec-package-v0.67.0
- aztec-package-v0.66.0
- aztec-package-v0.65.2
- aztec-package-v0.65.1
- aztec-package-v0.65.0
- aztec-package-v0.64.0
- aztec-package-v0.63.1
- aztec-package-v0.63.0
- aztec-package-v0.62.0
- aztec-package-v0.61.0
- aztec-package-v0.60.0
- aztec-package-v0.59.0
- aztec-package-v0.58.0
- aztec-package-v0.57.0
- aztec-package-v0.56.0
- aztec-package-v0.55.1
- aztec-package-v0.55.0
- aztec-package-v0.54.0
- aztec-package-v0.53.0
- aztec-package-v0.52.0
- aztec-package-v0.51.1
- aztec-package-v0.51.0
- aztec-package-v0.50.1
- aztec-package-v0.50.0
- aztec-package-v0.49.2
- aztec-package-v0.49.1
- aztec-package-v0.49.0
- aztec-package-v0.48.0
- aztec-package-v0.47.1
- aztec-package-v0.47.0
- aztec-package-v0.46.7
- aztec-package-v0.46.6
- aztec-package-v0.46.5
- aztec-package-v0.46.4
- aztec-package-v0.46.3
- aztec-package-v0.46.2
- aztec-package-v0.46.1
- aztec-package-v0.46.0
- aztec-package-v0.45.1
- aztec-package-v0.45.0
- aztec-package-v0.44.0
- aztec-package-v0.43.0
- aztec-package-v0.42.0
- aztec-package-v0.41.0
- aztec-package-v0.40.1
- aztec-package-v0.40.0
- aztec-package-v0.39.0
- aztec-package-v0.38.0
- aztec-package-v0.37.0
- aztec-package-v0.36.0
- aztec-package-v0.35.1
- aztec-package-v0.35.0
- aztec-package-v0.34.0
- aztec-package-v0.33.0
- aztec-package-v0.32.1
- aztec-package-v0.32.0
- aztec-package-v0.31.0
- aztec-package-v0.30.1
- aztec-package-v0.30.0
- aztec-package-v0.29.0
- aztec-package-v0.28.1
- aztec-package-v0.28.0
- aztec-package-v0.27.2
- aztec-package-v0.27.1
- aztec-package-v0.27.0
- aztec-package-v0.26.6
- aztec-package-v0.26.5
- aztec-package-v0.26.4
- aztec-package-v0.26.3
- aztec-package-v0.26.2
- aztec-package-v0.26.1
- aztec-package-v0.26.0
- aztec-cli-v0.35.1
- aztec-cli-v0.35.0
- aztec-cli-v0.34.0
- aztec-cli-v0.33.0
- aztec-cli-v0.32.1
- aztec-cli-v0.32.0
- aztec-cli-v0.31.0
- aztec-cli-v0.30.1
- aztec-cli-v0.30.0
- aztec-cli-v0.29.0
- aztec-cli-v0.28.1
- aztec-cli-v0.28.0
- aztec-cli-v0.27.2
- aztec-cli-v0.27.1
- aztec-cli-v0.27.0
- aztec-cli-v0.26.6
- aztec-cli-v0.26.5
- aztec-cli-v0.26.4
- aztec-cli-v0.26.3
- aztec-cli-v0.26.2
- aztec-cli-v0.26.1
- aztec-cli-v0.26.0
1 parent
ce90b1f
commit 9084b89
Showing
7 changed files
with
988 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
import { AztecNodeService } from '@aztec/aztec-node'; | ||
import { AztecRPCServer } from '@aztec/aztec-rpc'; | ||
import { AztecAddress, Wallet } from '@aztec/aztec.js'; | ||
import { DebugLogger } from '@aztec/foundation/log'; | ||
import { CardGameContract } from '@aztec/noir-contracts/types'; | ||
import { AztecRPC, CompleteAddress } from '@aztec/types'; | ||
|
||
import { setup } from './fixtures/utils.js'; | ||
|
||
/* eslint-disable camelcase */ | ||
|
||
interface Card { | ||
points: bigint; | ||
strength: bigint; | ||
} | ||
|
||
const cardToField = (card: Card): bigint => { | ||
return card.strength + card.points * 65536n; | ||
}; | ||
|
||
interface PlayerGameEntry { | ||
address: bigint; | ||
deck_strength: bigint; | ||
points: bigint; | ||
} | ||
|
||
interface Game { | ||
players: PlayerGameEntry[]; | ||
rounds_cards: Card[]; | ||
started: boolean; | ||
finished: boolean; | ||
claimed: boolean; | ||
current_player: bigint; | ||
current_round: bigint; | ||
} | ||
|
||
interface NoirOption<T> { | ||
_is_some: boolean; | ||
_value: T; | ||
} | ||
|
||
function unwrapOptions<T>(options: NoirOption<T>[]): T[] { | ||
return options.filter((option: any) => option._is_some).map((option: any) => option._value); | ||
} | ||
|
||
const GAME_ID = 42; | ||
|
||
describe('e2e_card_game', () => { | ||
let aztecNode: AztecNodeService | undefined; | ||
let aztecRpcServer: AztecRPC; | ||
let wallet: Wallet; | ||
let logger: DebugLogger; | ||
let firstPlayer: AztecAddress; | ||
let secondPlayer: AztecAddress; | ||
let thirdPlayer: AztecAddress; | ||
|
||
let contract: CardGameContract; | ||
|
||
beforeEach(async () => { | ||
let accounts: CompleteAddress[]; | ||
({ aztecNode, aztecRpcServer, accounts, wallet, logger } = await setup(3)); | ||
firstPlayer = accounts[0].address; | ||
secondPlayer = accounts[1].address; | ||
thirdPlayer = accounts[2].address; | ||
await deployContract(); | ||
}, 100_000); | ||
|
||
afterEach(async () => { | ||
await aztecNode?.stop(); | ||
if (aztecRpcServer instanceof AztecRPCServer) { | ||
await aztecRpcServer?.stop(); | ||
} | ||
}); | ||
|
||
const deployContract = async () => { | ||
logger(`Deploying L2 contract...`); | ||
contract = await CardGameContract.deploy(wallet).send().deployed(); | ||
logger(`L2 contract deployed at ${contract.address}`); | ||
}; | ||
|
||
const firstPlayerCollection: Card[] = [ | ||
{ | ||
points: 45778n, | ||
strength: 7074n, | ||
}, | ||
{ | ||
points: 60338n, | ||
strength: 53787n, | ||
}, | ||
{ | ||
points: 13035n, | ||
strength: 45778n, | ||
}, | ||
]; | ||
|
||
it('should be able to buy packs', async () => { | ||
await contract.methods.buy_pack(27n).send({ origin: firstPlayer }).wait(); | ||
const collection = await contract.methods.view_collection_cards(firstPlayer, 0).view({ from: firstPlayer }); | ||
expect(unwrapOptions(collection)).toEqual(firstPlayerCollection); | ||
}, 30_000); | ||
|
||
describe('game join', () => { | ||
beforeEach(async () => { | ||
await Promise.all([ | ||
contract.methods.buy_pack(27n).send({ origin: firstPlayer }).wait(), | ||
contract.methods.buy_pack(27n).send({ origin: secondPlayer }).wait(), | ||
]); | ||
}, 30_000); | ||
|
||
it('should be able to join games', async () => { | ||
await contract.methods | ||
.join_game(GAME_ID, [cardToField(firstPlayerCollection[0]), cardToField(firstPlayerCollection[2])]) | ||
.send({ origin: firstPlayer }) | ||
.wait(); | ||
|
||
await expect( | ||
contract.methods | ||
.join_game(GAME_ID, [cardToField(firstPlayerCollection[0]), cardToField(firstPlayerCollection[1])]) | ||
.send({ origin: secondPlayer }) | ||
.wait(), | ||
).rejects.toThrow(/Card not found/); | ||
|
||
const collection = await contract.methods.view_collection_cards(firstPlayer, 0).view({ from: firstPlayer }); | ||
expect(unwrapOptions(collection)).toEqual([ | ||
{ | ||
points: 60338n, | ||
strength: 53787n, | ||
}, | ||
]); | ||
|
||
expect((await contract.methods.view_game(GAME_ID).view({ from: firstPlayer })) as Game).toMatchObject({ | ||
players: [ | ||
{ | ||
address: firstPlayer.toBigInt(), | ||
deck_strength: 52852n, | ||
points: 0n, | ||
}, | ||
{ | ||
address: 0n, | ||
deck_strength: 0n, | ||
points: 0n, | ||
}, | ||
], | ||
started: false, | ||
finished: false, | ||
claimed: false, | ||
current_player: 0n, | ||
}); | ||
}, 30_000); | ||
|
||
it('should start games', async () => { | ||
const secondPlayerCollection = unwrapOptions( | ||
(await contract.methods | ||
.view_collection_cards(secondPlayer, 0) | ||
.view({ from: secondPlayer })) as NoirOption<Card>[], | ||
); | ||
|
||
await Promise.all([ | ||
contract.methods | ||
.join_game(GAME_ID, [cardToField(firstPlayerCollection[0]), cardToField(firstPlayerCollection[2])]) | ||
.send({ origin: firstPlayer }) | ||
.wait(), | ||
contract.methods | ||
.join_game(GAME_ID, [cardToField(secondPlayerCollection[0]), cardToField(secondPlayerCollection[2])]) | ||
.send({ origin: secondPlayer }) | ||
.wait(), | ||
]); | ||
|
||
await contract.methods.start_game(GAME_ID).send({ origin: firstPlayer }).wait(); | ||
|
||
expect((await contract.methods.view_game(GAME_ID).view({ from: firstPlayer })) as Game).toMatchObject({ | ||
players: expect.arrayContaining([ | ||
{ | ||
address: firstPlayer.toBigInt(), | ||
deck_strength: 52852n, | ||
points: 0n, | ||
}, | ||
{ | ||
address: secondPlayer.toBigInt(), | ||
deck_strength: expect.anything(), | ||
points: 0n, | ||
}, | ||
]), | ||
started: true, | ||
finished: false, | ||
claimed: false, | ||
current_player: 0n, | ||
}); | ||
}, 30_000); | ||
}); | ||
|
||
describe('game play', () => { | ||
let secondPlayerCollection: Card[]; | ||
let thirdPlayerCOllection: Card[]; | ||
|
||
beforeEach(async () => { | ||
await Promise.all([ | ||
contract.methods.buy_pack(27n).send({ origin: firstPlayer }).wait(), | ||
contract.methods.buy_pack(27n).send({ origin: secondPlayer }).wait(), | ||
contract.methods.buy_pack(27n).send({ origin: thirdPlayer }).wait(), | ||
]); | ||
|
||
secondPlayerCollection = unwrapOptions( | ||
await contract.methods.view_collection_cards(secondPlayer, 0).view({ from: secondPlayer }), | ||
); | ||
|
||
thirdPlayerCOllection = unwrapOptions( | ||
await contract.methods.view_collection_cards(thirdPlayer, 0).view({ from: thirdPlayer }), | ||
); | ||
}, 60_000); | ||
|
||
async function joinGame(playerAddress: AztecAddress, cards: Card[], id = GAME_ID) { | ||
await contract.methods.join_game(id, cards.map(cardToField)).send({ origin: playerAddress }).wait(); | ||
} | ||
|
||
async function playGame(playerDecks: { address: AztecAddress; deck: Card[] }[], id = GAME_ID) { | ||
const initialGameState = (await contract.methods.view_game(id).view({ from: firstPlayer })) as Game; | ||
const players = initialGameState.players.map(player => player.address); | ||
const cards = players.map( | ||
player => playerDecks.find(playerDeckEntry => playerDeckEntry.address.toBigInt() === player)!.deck, | ||
); | ||
|
||
for (let roundIndex = 0; roundIndex < cards.length; roundIndex++) { | ||
for (let playerIndex = 0; playerIndex < players.length; playerIndex++) { | ||
const player = players[playerIndex]; | ||
const card = cards[playerIndex][roundIndex]; | ||
await contract.methods | ||
.play_card(id, card) | ||
.send({ origin: AztecAddress.fromBigInt(player) }) | ||
.wait(); | ||
} | ||
} | ||
|
||
const finalGameState = (await contract.methods.view_game(id).view({ from: firstPlayer })) as Game; | ||
|
||
expect(finalGameState.finished).toBe(true); | ||
return finalGameState; | ||
} | ||
|
||
it('should play a game, claim the winned cards and play another match with winned cards', async () => { | ||
const firstPlayerGameDeck = [firstPlayerCollection[0], firstPlayerCollection[2]]; | ||
const secondPlayerGameDeck = [secondPlayerCollection[0], secondPlayerCollection[2]]; | ||
await Promise.all([joinGame(firstPlayer, firstPlayerGameDeck), joinGame(secondPlayer, secondPlayerGameDeck)]); | ||
await contract.methods.start_game(GAME_ID).send({ origin: firstPlayer }).wait(); | ||
|
||
let game = await playGame([ | ||
{ address: firstPlayer, deck: firstPlayerGameDeck }, | ||
{ address: secondPlayer, deck: secondPlayerGameDeck }, | ||
]); | ||
|
||
const sotedByPoints = game.players.sort((a, b) => Number(b.points - a.points)); | ||
const winner = AztecAddress.fromBigInt(sotedByPoints[0].address); | ||
const loser = AztecAddress.fromBigInt(sotedByPoints[1].address); | ||
|
||
await expect( | ||
contract.methods.claim_cards(GAME_ID, game.rounds_cards.map(cardToField)).send({ origin: loser }).wait(), | ||
).rejects.toThrow(/Not the winner/); | ||
|
||
await contract.methods.claim_cards(GAME_ID, game.rounds_cards.map(cardToField)).send({ origin: winner }).wait(); | ||
|
||
const winnerCollection = unwrapOptions( | ||
(await contract.methods.view_collection_cards(winner, 0).view({ from: winner })) as NoirOption<Card>[], | ||
); | ||
|
||
const winnerGameDeck = [winnerCollection[0], winnerCollection[3]]; | ||
const thirdPlayerGameDeck = [thirdPlayerCOllection[0], thirdPlayerCOllection[2]]; | ||
|
||
await Promise.all([ | ||
joinGame(winner, winnerGameDeck, GAME_ID + 1), | ||
joinGame(thirdPlayer, thirdPlayerGameDeck, GAME_ID + 1), | ||
]); | ||
|
||
await contract.methods | ||
.start_game(GAME_ID + 1) | ||
.send({ origin: winner }) | ||
.wait(); | ||
game = await playGame( | ||
[ | ||
{ address: winner, deck: winnerGameDeck }, | ||
{ address: thirdPlayer, deck: thirdPlayerGameDeck }, | ||
], | ||
GAME_ID + 1, | ||
); | ||
|
||
expect(game.finished).toBe(true); | ||
}, 180_000); | ||
}); | ||
}); |
9 changes: 9 additions & 0 deletions
9
yarn-project/noir-contracts/src/contracts/card_game_contract/Nargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "card_game_contract" | ||
authors = [""] | ||
compiler_version = "0.1" | ||
type = "contract" | ||
|
||
[dependencies] | ||
aztec = { path = "../../../../noir-libs/noir-aztec" } | ||
value_note = { path = "../../../../noir-libs/value-note"} |
235 changes: 235 additions & 0 deletions
235
yarn-project/noir-contracts/src/contracts/card_game_contract/src/cards.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
use dep::aztec::{ | ||
context::{PrivateContext, PublicContext}, | ||
constants_gen::{MAX_NOTES_PER_PAGE, MAX_READ_REQUESTS_PER_CALL}, | ||
log::emit_encrypted_log, | ||
note::{ | ||
note_getter_options::NoteGetterOptions, | ||
note_viewer_options::NoteViewerOptions, | ||
note_getter::view_notes, | ||
}, | ||
oracle::{ | ||
get_public_key::get_public_key, | ||
get_secret_key::get_secret_key, | ||
}, | ||
state_vars::set::Set, | ||
types::point::Point, | ||
}; | ||
use dep::std; | ||
use dep::std::{ | ||
option::Option, | ||
}; | ||
use dep::value_note::{ | ||
value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}, | ||
}; | ||
|
||
struct Card { | ||
strength: u16, | ||
points: u16, | ||
} | ||
|
||
impl Card { | ||
fn from_field(field: Field) -> Card { | ||
let value_bytes = field.to_le_bytes(32); | ||
let strength = (value_bytes[0] as u16) + (value_bytes[1] as u16) * 256; | ||
let points = (value_bytes[2] as u16) + (value_bytes[3] as u16) * 256; | ||
Card { | ||
strength, | ||
points, | ||
} | ||
} | ||
|
||
fn to_field(self) -> Field { | ||
self.strength as Field + (self.points as Field)*65536 | ||
} | ||
|
||
fn serialize(self) -> [Field; 2] { | ||
[self.strength as Field, self.points as Field] | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_to_from_field() { | ||
let field = 1234567890; | ||
let card = Card::from_field(field); | ||
assert(card.to_field() == field); | ||
} | ||
|
||
|
||
struct CardNote { | ||
card: Card, | ||
note: ValueNote, | ||
} | ||
|
||
impl CardNote { | ||
fn new( | ||
strength: u16, | ||
points: u16, | ||
owner: Field, | ||
) -> Self { | ||
let card = Card { | ||
strength, | ||
points, | ||
}; | ||
CardNote::from_card(card, owner) | ||
} | ||
|
||
fn from_card(card: Card, owner: Field) -> CardNote { | ||
CardNote { | ||
card, | ||
note: ValueNote::new(card.to_field(), owner), | ||
} | ||
} | ||
|
||
fn from_note(note: ValueNote) -> CardNote { | ||
CardNote { | ||
card: Card::from_field(note.value), | ||
note, | ||
} | ||
} | ||
} | ||
|
||
struct Deck { | ||
set: Set<ValueNote, VALUE_NOTE_LEN>, | ||
} | ||
|
||
fn filter_cards<N>(notes: [Option<ValueNote>; MAX_READ_REQUESTS_PER_CALL], desired_cards: [Card; N]) -> [Option<ValueNote>; MAX_READ_REQUESTS_PER_CALL] { | ||
let mut selected = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; | ||
|
||
let mut found = [false; N]; | ||
|
||
for i in 0..notes.len() { | ||
let note = notes[i]; | ||
if note.is_some() { | ||
let card_note = CardNote::from_note( | ||
note.unwrap_unchecked() | ||
); | ||
for j in 0..N { | ||
if !found[j] & (card_note.card.strength == desired_cards[j].strength) & (card_note.card.points == desired_cards[j].points) { | ||
selected[i] = note; | ||
found[j] = true; | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
||
selected | ||
} | ||
|
||
|
||
impl Deck { | ||
fn new( | ||
private_context: Option<&mut PrivateContext>, | ||
public_context: Option<&mut PublicContext>, | ||
storage_slot: Field, | ||
) -> Self { | ||
let set = Set { | ||
private_context, | ||
public_context, | ||
storage_slot, | ||
note_interface: ValueNoteMethods, | ||
}; | ||
Deck { | ||
set | ||
} | ||
} | ||
|
||
fn add_cards<N>(&mut self, cards: [Card; N], owner: Field) -> [CardNote]{ | ||
let owner_key = get_public_key(owner); | ||
let context = self.set.private_context.unwrap(); | ||
|
||
let mut inserted_cards = []; | ||
for card in cards { | ||
let mut card_note = CardNote::from_card(card, owner); | ||
self.set.insert(&mut card_note.note); | ||
emit_encrypted_log( | ||
context, | ||
(*context).this_address(), | ||
self.set.storage_slot, | ||
owner_key, | ||
card_note.note.serialise(), | ||
); | ||
inserted_cards = inserted_cards.push_back(card_note); | ||
} | ||
|
||
inserted_cards | ||
} | ||
|
||
fn get_cards<N>(&mut self, cards: [Card; N], owner: Field) -> [CardNote; N] { | ||
let options = NoteGetterOptions::with_filter(filter_cards, cards); | ||
let maybe_notes = self.set.get_notes(options); | ||
let mut found_cards = [Option::none(); N]; | ||
for i in 0..maybe_notes.len() { | ||
if maybe_notes[i].is_some() { | ||
let card_note = CardNote::from_note( | ||
maybe_notes[i].unwrap_unchecked() | ||
); | ||
// Ensure the notes are actually owned by the owner (to prevent user from generating a valid proof while | ||
// spending someone else's notes). | ||
assert(card_note.note.owner == owner); | ||
|
||
for j in 0..cards.len() { | ||
if found_cards[j].is_none() & (cards[j].strength == card_note.card.strength) & (cards[j].points == card_note.card.points) { | ||
found_cards[j] = Option::some(card_note); | ||
} | ||
} | ||
} | ||
} | ||
|
||
found_cards.map(|card_note: Option<CardNote>| { | ||
assert(card_note.is_some(), "Card not found"); | ||
card_note.unwrap_unchecked() | ||
}) | ||
} | ||
|
||
fn remove_cards<N>(&mut self, cards: [Card; N], owner: Field) { | ||
let card_notes = self.get_cards(cards, owner); | ||
for card_note in card_notes { | ||
self.set.remove(card_note.note); | ||
} | ||
} | ||
|
||
unconstrained fn view_cards(self, offset: u32) -> [Option<Card>; MAX_NOTES_PER_PAGE] { | ||
let options = NoteViewerOptions::new().set_offset(offset); | ||
let opt_notes = self.set.view_notes(options); | ||
let mut opt_cards = [Option::none(); MAX_NOTES_PER_PAGE]; | ||
|
||
for i in 0..opt_notes.len() { | ||
opt_cards[i] = opt_notes[i].map(|note: ValueNote| Card::from_field(note.value)); | ||
} | ||
|
||
opt_cards | ||
} | ||
|
||
} | ||
|
||
global PACK_CARDS = 3; // Limited by number of write requests (max 4) | ||
|
||
fn get_pack_cards( | ||
seed: Field, | ||
owner_address: Field | ||
) -> [Card; PACK_CARDS] { | ||
// generate pseudo randomness deterministically from 'seed' and user secret | ||
let secret = get_secret_key(owner_address); | ||
let mix = secret.high + secret.low + seed; | ||
let random_bytes = std::hash::sha256(mix.to_le_bytes(32)); | ||
|
||
let mut cards = [Card::from_field(0); PACK_CARDS]; | ||
// we generate PACK_CARDS cards | ||
assert((PACK_CARDS as u64) < 8, "Cannot generate more than 8 cards"); | ||
for i in 0..PACK_CARDS { | ||
let strength = (random_bytes[i] as u16) + (random_bytes[i + 1] as u16) * 256; | ||
let points = (random_bytes[i + 2] as u16) + (random_bytes[i + 3] as u16) * 256; | ||
cards[i] = Card { | ||
strength, points | ||
}; | ||
} | ||
|
||
cards | ||
} | ||
|
||
fn compute_deck_strength<N>(cards: [Card; N]) -> Field { | ||
cards.fold(0, |acc, card: Card| { | ||
acc + card.strength as Field | ||
}) | ||
} |
172 changes: 172 additions & 0 deletions
172
yarn-project/noir-contracts/src/contracts/card_game_contract/src/game.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
use dep::aztec::types::type_serialisation::TypeSerialisationInterface; | ||
use crate::cards::Card; | ||
|
||
global NUMBER_OF_PLAYERS = 2; | ||
global NUMBER_OF_CARDS_DECK = 2; | ||
|
||
struct PlayerEntry { | ||
address: Field, | ||
deck_strength: u32, | ||
points: u120, | ||
} | ||
|
||
impl PlayerEntry { | ||
fn is_initialised(self) -> bool { | ||
self.address != 0 | ||
} | ||
} | ||
|
||
global PLAYABLE_CARDS = 4; | ||
|
||
struct Game { | ||
players: [PlayerEntry; NUMBER_OF_PLAYERS], | ||
rounds_cards: [Card; PLAYABLE_CARDS], | ||
started: bool, | ||
finished: bool, | ||
claimed: bool, | ||
current_player: u32, | ||
current_round: u32, | ||
} | ||
|
||
global GAME_SERIALISED_LEN: Field = 15; | ||
|
||
fn deserialiseGame(fields: [Field; GAME_SERIALISED_LEN]) -> Game { | ||
let players = [ | ||
PlayerEntry { | ||
address: fields[0], | ||
deck_strength: fields[1] as u32, | ||
points: fields[2] as u120, | ||
}, | ||
PlayerEntry { | ||
address: fields[3], | ||
deck_strength: fields[4] as u32, | ||
points: fields[5] as u120, | ||
}, | ||
]; | ||
let rounds_cards = [ | ||
Card::from_field(fields[6]), Card::from_field(fields[7]), | ||
Card::from_field(fields[8]), Card::from_field(fields[9]), | ||
]; | ||
Game { | ||
players, | ||
rounds_cards, | ||
started: fields[10] as bool, | ||
finished: fields[11] as bool, | ||
claimed: fields[12] as bool, | ||
current_player: fields[13] as u32, | ||
current_round: fields[14] as u32, | ||
} | ||
} | ||
|
||
fn serialiseGame(game: Game) -> [Field; GAME_SERIALISED_LEN] { | ||
[ | ||
game.players[0].address, | ||
game.players[0].deck_strength as Field, | ||
game.players[0].points as Field, | ||
game.players[1].address, | ||
game.players[1].deck_strength as Field, | ||
game.players[1].points as Field, | ||
game.rounds_cards[0].to_field(), | ||
game.rounds_cards[1].to_field(), | ||
game.rounds_cards[2].to_field(), | ||
game.rounds_cards[3].to_field(), | ||
game.started as Field, | ||
game.finished as Field, | ||
game.claimed as Field, | ||
game.current_player as Field, | ||
game.current_round as Field, | ||
] | ||
} | ||
|
||
impl Game { | ||
fn serialize(self: Self) -> [Field; GAME_SERIALISED_LEN] { | ||
serialiseGame(self) | ||
} | ||
|
||
fn add_player(&mut self, player_entry: PlayerEntry) -> bool { | ||
let mut added = false; | ||
|
||
for i in 0..NUMBER_OF_PLAYERS { | ||
let entry = self.players[i]; | ||
if entry.is_initialised() { | ||
assert(entry.address != player_entry.address, "Player already in game"); | ||
} else if !added { | ||
self.players[i] = player_entry; | ||
added = true; | ||
} | ||
} | ||
|
||
added | ||
} | ||
|
||
fn start_game(&mut self) { | ||
assert(!self.started, "Game already started"); | ||
for i in 0..NUMBER_OF_PLAYERS { | ||
let entry = self.players[i]; | ||
assert(entry.is_initialised(), "Game not full"); | ||
} | ||
let sorted_by_deck_strength = self.players.sort_via(|a: PlayerEntry, b: PlayerEntry| a.deck_strength < b.deck_strength); | ||
self.players = sorted_by_deck_strength; | ||
self.started = true; | ||
} | ||
|
||
fn current_player(self) -> PlayerEntry { | ||
assert(self.started, "Game not started"); | ||
assert(!self.finished, "Game finished"); | ||
self.players[self.current_player] | ||
} | ||
|
||
fn winner(self) -> PlayerEntry { | ||
assert(self.finished, "Game not finished"); | ||
let mut winner = self.players[0]; | ||
for i in 1..NUMBER_OF_PLAYERS { | ||
let entry = self.players[i]; | ||
if entry.points > winner.points { | ||
winner = entry; | ||
} | ||
} | ||
winner | ||
} | ||
|
||
fn play_card(&mut self, card: Card) { | ||
assert(self.started, "Game not started"); | ||
assert(!self.finished, "Game finished"); | ||
|
||
let round_offset = self.current_round * NUMBER_OF_PLAYERS; | ||
|
||
self.rounds_cards[round_offset + self.current_player] = card; | ||
self.current_player = (self.current_player + 1) % NUMBER_OF_PLAYERS; | ||
|
||
if self.current_player == 0 { | ||
self._finish_round(); | ||
} | ||
} | ||
|
||
fn _finish_round(&mut self) { | ||
let round_offset = self.current_round * NUMBER_OF_PLAYERS; | ||
self.current_round += 1; | ||
|
||
let mut winner_index = 0; | ||
let mut winner_strength = 0; | ||
let mut round_points = 0; | ||
|
||
for i in 0..NUMBER_OF_PLAYERS { | ||
let card = self.rounds_cards[round_offset + i]; | ||
round_points += (card.points as u120); | ||
if card.strength > winner_strength { | ||
winner_strength = card.strength; | ||
winner_index = i; | ||
} | ||
} | ||
|
||
self.players[winner_index].points += round_points; | ||
if self.current_round == NUMBER_OF_CARDS_DECK { | ||
self.finished = true; | ||
} | ||
} | ||
} | ||
|
||
global GameSerialisationMethods = TypeSerialisationInterface { | ||
deserialise: deserialiseGame, | ||
serialise: serialiseGame, | ||
}; |
270 changes: 270 additions & 0 deletions
270
yarn-project/noir-contracts/src/contracts/card_game_contract/src/main.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
mod cards; | ||
mod game; | ||
|
||
use dep::aztec::{ | ||
context::{PrivateContext, PublicContext}, | ||
state_vars::{ | ||
map::Map, | ||
public_state::PublicState, | ||
}, | ||
}; | ||
|
||
use dep::std::option::Option; | ||
|
||
use cards::{Deck}; | ||
use game::{Game, GameSerialisationMethods, GAME_SERIALISED_LEN}; | ||
|
||
struct Storage { | ||
collections: Map<Deck>, | ||
game_decks: Map<Map<Deck>>, | ||
games: Map<PublicState<Game, GAME_SERIALISED_LEN>>, | ||
} | ||
|
||
impl Storage { | ||
fn init( | ||
private_context: Option<&mut PrivateContext>, | ||
public_context: Option<&mut PublicContext>, | ||
) -> Self { | ||
Storage { | ||
collections: Map::new( | ||
private_context, | ||
public_context, | ||
1, | ||
|private_context, public_context, slot| { | ||
Deck::new( | ||
private_context, | ||
public_context, | ||
slot, | ||
) | ||
}, | ||
), | ||
game_decks: Map::new( | ||
private_context, | ||
public_context, | ||
2, | ||
|private_context, public_context, slot| { | ||
Map::new( | ||
private_context, | ||
public_context, | ||
slot, | ||
|private_context, public_context, slot|{ | ||
Deck::new( | ||
private_context, | ||
public_context, | ||
slot, | ||
) | ||
} | ||
) | ||
}, | ||
), | ||
games: Map::new( | ||
private_context, | ||
public_context, | ||
3, | ||
|private_context, public_context, slot| { | ||
PublicState::new( | ||
private_context, | ||
public_context, | ||
slot, | ||
GameSerialisationMethods, | ||
) | ||
}, | ||
) | ||
} | ||
} | ||
} | ||
|
||
contract CardGame { | ||
use dep::std::option::Option; | ||
use dep::value_note::{ | ||
balance_utils, | ||
value_note::{ | ||
ValueNoteMethods, | ||
VALUE_NOTE_LEN, | ||
}, | ||
}; | ||
|
||
use dep::aztec::{ | ||
abi, | ||
constants_gen::{MAX_NOTES_PER_PAGE}, | ||
abi::{ | ||
Hasher, PrivateContextInputs, | ||
}, | ||
context::PrivateContext, | ||
note::{ | ||
note_header::NoteHeader, | ||
utils as note_utils, | ||
}, | ||
oracle::compute_selector::compute_selector | ||
}; | ||
|
||
use crate::Storage; | ||
use crate::cards::{ | ||
PACK_CARDS, | ||
Deck, | ||
Card, | ||
get_pack_cards, | ||
compute_deck_strength, | ||
}; | ||
use crate::game::{ | ||
NUMBER_OF_PLAYERS, | ||
NUMBER_OF_CARDS_DECK, | ||
PLAYABLE_CARDS, | ||
PlayerEntry, | ||
Game | ||
}; | ||
|
||
#[aztec(private)] | ||
fn constructor() {} | ||
|
||
#[aztec(private)] | ||
fn buy_pack( | ||
seed: Field, // The randomness used to generate the cards. Passed in for now. | ||
) { | ||
let storage = Storage::init(Option::some(&mut context), Option::none()); | ||
let buyer = context.msg_sender(); | ||
let mut cards = get_pack_cards(seed, buyer); | ||
|
||
let mut collection = storage.collections.at(buyer); | ||
let _inserted_cards = collection.add_cards(cards, buyer); | ||
} | ||
|
||
#[aztec(private)] | ||
fn join_game( | ||
game: u32, | ||
cards_fields: [Field; 2], | ||
) { | ||
let cards = cards_fields.map(|card_field| Card::from_field(card_field)); | ||
let storage = Storage::init(Option::some(&mut context), Option::none()); | ||
let player = context.msg_sender(); | ||
|
||
let mut collection = storage.collections.at(player); | ||
collection.remove_cards(cards, player); | ||
let mut game_deck = storage.game_decks.at(game as Field).at(player); | ||
let _added_to_game_deck = game_deck.add_cards(cards, player); | ||
let selector = compute_selector("on_game_joined(u32,Field,u32)"); | ||
let strength = compute_deck_strength(cards); | ||
context.call_public_function(context.this_address(), selector, [game as Field, player, strength]); | ||
} | ||
|
||
#[aztec(public)] | ||
internal fn on_game_joined( | ||
game: u32, | ||
player: Field, | ||
deck_strength: u32, | ||
) { | ||
let storage = Storage::init(Option::none(), Option::some(&mut context)); | ||
let game_storage = storage.games.at(game as Field); | ||
|
||
let mut game_data = game_storage.read(); | ||
assert(game_data.add_player(PlayerEntry {address: player, deck_strength, points: 0}), "Game full"); | ||
|
||
game_storage.write(game_data); | ||
} | ||
|
||
#[aztec(public)] | ||
fn start_game(game: u32) { | ||
let storage = Storage::init(Option::none(), Option::some(&mut context)); | ||
let game_storage = storage.games.at(game as Field); | ||
|
||
let mut game_data = game_storage.read(); | ||
game_data.start_game(); | ||
game_storage.write(game_data); | ||
} | ||
|
||
#[aztec(private)] | ||
fn play_card( | ||
game: u32, | ||
card: Card, | ||
) { | ||
let storage = Storage::init(Option::some(&mut context), Option::none()); | ||
let player = context.msg_sender(); | ||
|
||
let mut game_deck = storage.game_decks.at(game as Field).at(player); | ||
game_deck.remove_cards([card], player); | ||
|
||
let selector = compute_selector("on_card_played(u32,Field,Field)"); | ||
context.call_public_function(context.this_address(), selector, [game as Field, player, card.to_field()]); | ||
} | ||
|
||
#[aztec(public)] | ||
internal fn on_card_played(game: u32, player: Field, card_as_field: Field) { | ||
let storage = Storage::init(Option::none(), Option::some(&mut context)); | ||
let game_storage = storage.games.at(game as Field); | ||
|
||
let mut game_data = game_storage.read(); | ||
|
||
let card = Card::from_field(card_as_field); | ||
let current_player = game_data.current_player(); | ||
assert(current_player.address == player, "Not your turn"); | ||
game_data.play_card(card); | ||
|
||
game_storage.write(game_data); | ||
} | ||
|
||
#[aztec(private)] | ||
fn claim_cards( | ||
game: u32, | ||
cards_fields: [Field; PLAYABLE_CARDS], | ||
) { | ||
let storage = Storage::init(Option::some(&mut context), Option::none()); | ||
let player = context.msg_sender(); | ||
let cards = cards_fields.map(|card_field| Card::from_field(card_field)); | ||
|
||
let mut collection = storage.collections.at(player); | ||
let _inserted_cards = collection.add_cards(cards, player); | ||
|
||
let selector = compute_selector("on_cards_claimed(u32,Field,Field)"); | ||
context.call_public_function( | ||
context.this_address(), | ||
selector, | ||
[game as Field, player, dep::std::hash::pedersen(cards_fields)[0]] | ||
); | ||
} | ||
|
||
#[aztec(public)] | ||
internal fn on_cards_claimed(game: u32, player: Field, cards_hash: Field) { | ||
let storage = Storage::init(Option::none(), Option::some(&mut context)); | ||
let game_storage = storage.games.at(game as Field); | ||
let mut game_data = game_storage.read(); | ||
|
||
assert(!game_data.claimed, "Already claimed"); | ||
game_data.claimed = true; | ||
|
||
assert_eq( | ||
cards_hash, | ||
dep::std::hash::pedersen(game_data.rounds_cards.map(|card: Card| card.to_field()))[0] | ||
); | ||
|
||
let winner = game_data.winner(); | ||
assert_eq(player, winner.address, "Not the winner"); | ||
|
||
game_storage.write(game_data); | ||
} | ||
|
||
unconstrained fn view_collection_cards(owner: Field, offset: u32) -> [Option<Card>; MAX_NOTES_PER_PAGE] { | ||
let storage = Storage::init(Option::none(), Option::none()); | ||
let collection = storage.collections.at(owner); | ||
|
||
collection.view_cards(offset) | ||
} | ||
|
||
unconstrained fn view_game_cards(game: u32, player: Field, offset: u32) -> [Option<Card>; MAX_NOTES_PER_PAGE] { | ||
let storage = Storage::init(Option::none(), Option::none()); | ||
let game_deck = storage.game_decks.at(game as Field).at(player); | ||
|
||
game_deck.view_cards(offset) | ||
} | ||
|
||
unconstrained fn view_game(game: u32) -> Game { | ||
Storage::init(Option::none(), Option::none()).games.at(game as Field).read() | ||
} | ||
|
||
// Computes note hash and nullifier. | ||
// Note 1: Needs to be defined by every contract producing logs. | ||
// Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. | ||
unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { | ||
let note_header = NoteHeader { contract_address, nonce, storage_slot }; | ||
note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) | ||
} | ||
} |