diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e595012f7..515b75d50 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -355,11 +355,26 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly - - name: Install tarpaulin - run: cargo +nightly install cargo-tarpaulin - - name: Run tarpaulin - run: cargo tarpaulin --no-dead-code --engine llvm --workspace --all-features --ignore-panics --out Lcov + toolchain: stable + - name: Run for coverage + run: | + sudo apt-get update + sudo apt-get install lcov -y + rustup component add llvm-tools-preview + cargo install grcov + export RUSTFLAGS="-Cinstrument-coverage" + export LLVM_PROFILE_FILE=$(pwd)/target/clvm_tools_rs-%p-%m.profraw + export CARGO_TARGET_DIR=$(pwd)/target + cargo test --release --workspace + python -m venv venv + source venv/bin/activate + git clone https://github.com/Chia-Network/clvm_tools.git --branch=main --single-branch + pip install ./clvm_tools + pip install maturin pytest + maturin develop --release + (cd resources/tests/cmdline/tests && pytest) + grcov . --binary-path target -s . --branch --ignore-not-existing --ignore='*/.cargo/*' --ignore='*/tests/*' -o rust_cov.info + python -c 'with open("rust_cov.info") as f: lines = [l for l in f if not (l.startswith("DA:") and int(l.split(",")[1].strip()) >= 2**63)]; open("lcov.info", "w").writelines(lines)' - name: Upload to Coveralls uses: coverallsapp/github-action@v2 if: always() diff --git a/.github/workflows/extensive-tests.yml b/.github/workflows/extensive-tests.yml new file mode 100644 index 000000000..6bf997da7 --- /dev/null +++ b/.github/workflows/extensive-tests.yml @@ -0,0 +1,120 @@ +# Taken from clvm_rs' version. +name: Extensive tests + +on: + push: + branches: + - main + - dev + tags: + - '**' + pull_request: + branches: + - '**' + +jobs: + extensive_tests: + name: Extensive tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up rusts + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt, clippy + + - name: Set up rust (stable) + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt, clippy + + - uses: actions/setup-python@v4 + name: Install Python 3.11 + with: + python-version: 3.11 + + - name: Update pip + run: | + python -m pip install --upgrade pip + + - name: Set up rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - name: Install dependencies + run: | + python -m pip install maturin==1.1.0 + + - name: Build Linux in manylinux2010 with maturin on Python ${{ matrix.python }} + run: | + podman run --rm=true \ + -v ${{ github.workspace }}:/ws:rw --workdir=/ws \ + ghcr.io/chia-network/build-images/centos-pypa-rust-x86_64 \ + bash -exc '\ + yum -y install libc6 openssl-devel && \ + source $HOME/.cargo/env && \ + rustup target add x86_64-unknown-linux-musl && \ + rm -rf venv && \ + PY_VERSION=${{ matrix.python }} + PY_VERSION=${PY_VERSION/.} && \ + echo "Python version with dot removed is $PY_VERSION" && \ + if [ "$PY_VERSION" = "37" ]; \ + then export SCND_VERSION="${PY_VERSION}m"; \ + else export SCND_VERSION="$PY_VERSION"; fi && \ + echo "Exporting path /opt/python/cp$PY_VERSION-cp$SCND_VERSION/bin" && \ + export PATH=/opt/python/cp$PY_VERSION-cp$SCND_VERSION/bin/:$PATH && \ + /opt/python/cp38-cp38/bin/python -m venv venv && \ + if [ ! -f "activate" ]; then ln -s venv/bin/activate; fi && \ + . ./activate && \ + pip install --upgrade pip + ' + docker run --rm -v $(pwd):/io ghcr.io/pyo3/maturin:v1.1.0 build --release --strip --manylinux 2014 + # Refresh in case any ownerships changed. + mv target target.docker && cp -r target.docker target + # Ensure an empty .cargo-lock file exists. + touch target/release/.cargo-lock + + - name: Install clvm_tools_rs wheel + if: ${{ !startsWith(matrix.os, 'windows') }} + run: | + . ./activate + ls target/wheels/ + # this mess puts the name of the `.whl` file into `$WHEEL_PATH` + # remove the dot, use the `glob` lib to grab the file from the directory + export WHEEL_PATH=$(echo ${{ matrix.python }} | python -c 'DOTLESS=input().replace(".", ""); import glob; print(" ".join(filter(lambda x: "musl" not in x, glob.glob("target/wheels/clvm_tools_rs-*-cp*-*.whl"))))' ) + echo ${WHEEL_PATH} + pip install ${WHEEL_PATH} + + - name: Install other wheels + run: | + . ./activate + python -m pip install pytest + python -m pip install blspy + + - name: install clvm & clvm_tools + run: | + . ./activate + git clone https://github.com/Chia-Network/clvm.git --branch=main --single-branch + python -m pip install ./clvm + + echo "installing clvm_rs via pip" + pip install clvm_rs + + echo "installing clvm_tools for clvm tests" + # clvm tools is required to run the tests is clvm + python -m pip install clvm_tools + + - name: Run game referee test + run: | + . ./activate + cp support/test-game-referee.sh . + sh test-game-referee.sh resources/tests/game-referee-in-cl21 diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 691fdff94..46a2d9fe6 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -78,7 +78,7 @@ jobs: SECRET: "${{ secrets.NPM_TOKEN }}" - name: Publish wasm - if: env.FULL_RELEASE == 'true' && steps.check_secrets.HAS_SECRET + if: env.FULL_RELEASE == 'true' && steps.check_secrets.outputs.HAS_SECRET env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} working-directory: ${{ github.workspace }}/wasm/pkg diff --git a/.gitignore b/.gitignore index 77e2b42a7..a343936cb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,9 @@ wasm/tests/node_modules resources/tests/bridgeref/*.sym resources/tests/bridgeref/*.clvm.hex resources/tests/gameref21/*.sym -resources/tests/gameref21/*.clvm.hex \ No newline at end of file +resources/tests/gameref21/*.clvm.hex +resources/tests/gameref21/*.clvm.hex +resources/tests/game-referee/*.sym +resources/tests/game-referee/*.clvm.hex +__pycache__ +.pytest_cache diff --git a/CHANGELOG.md b/CHANGELOG.md index e50a9fa5b..a1b58b09b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,3 +30,7 @@ Skipped - &rest arguments. - new bls and sec256 operators. +## 0.1.36 + +- modern lambda added +- updated some internal data strucutres and call interfaces to support env variable renaming at during closure generation / lambda capture, or any step during transformation. diff --git a/Cargo.lock b/Cargo.lock index 2afc51d86..e24af67df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clvm_tools_rs" -version = "0.1.35" +version = "0.1.36" dependencies = [ "binascii", "bls12_381", diff --git a/Cargo.toml b/Cargo.toml index bf4b61e85..673dbf0e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clvm_tools_rs" -version = "0.1.35" +version = "0.1.36" edition = "2018" authors = ["Art Yerkes "] description = "tools for working with chialisp language; compiler, repl, python and wasm bindings" diff --git a/resources/tests/game-referee-in-cl21/all-in-list.clinc b/resources/tests/game-referee-in-cl21/all-in-list.clinc new file mode 100644 index 000000000..1db5f26ad --- /dev/null +++ b/resources/tests/game-referee-in-cl21/all-in-list.clinc @@ -0,0 +1,12 @@ +( + (defun enquote-rest (L) + (if L + (c (c 1 (f L)) (enquote-rest (r L))) + () + ) + ) + + (defun all-in-list (L) + (a (c 34 (enquote-rest L)) ()) + ) +) diff --git a/resources/tests/game-referee-in-cl21/assert.clinc b/resources/tests/game-referee-in-cl21/assert.clinc new file mode 100644 index 000000000..c9f212394 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/assert.clinc @@ -0,0 +1,8 @@ +( + (defmacro assert items + (if (r items) + (list if (f items) (c assert (r items)) (q . (x))) + (f items) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/busy.clinc b/resources/tests/game-referee-in-cl21/busy.clinc new file mode 100644 index 000000000..e534d2125 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/busy.clinc @@ -0,0 +1,11 @@ +( + (defun busy (myfunc mylist returnval) + (if mylist + (last + (a myfunc (list (f mylist))) + (busy myfunc (r mylist) returnval) + ) + returnval + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/condition_codes.clinc b/resources/tests/game-referee-in-cl21/condition_codes.clinc new file mode 100644 index 000000000..45f3265da --- /dev/null +++ b/resources/tests/game-referee-in-cl21/condition_codes.clinc @@ -0,0 +1,41 @@ +; See chia/types/condition_opcodes.py + +( + (defconstant AGG_SIG_UNSAFE 49) + (defconstant AGG_SIG_ME 50) + + ; the conditions below reserve coin amounts and have to be accounted for in output totals + + (defconstant CREATE_COIN 51) + (defconstant RESERVE_FEE 52) + + ; the conditions below deal with announcements, for inter-coin communication + + ; coin announcements + (defconstant CREATE_COIN_ANNOUNCEMENT 60) + (defconstant ASSERT_COIN_ANNOUNCEMENT 61) + + ; puzzle announcements + (defconstant CREATE_PUZZLE_ANNOUNCEMENT 62) + (defconstant ASSERT_PUZZLE_ANNOUNCEMENT 63) + + ; the conditions below let coins inquire about themselves + + (defconstant ASSERT_MY_COIN_ID 70) + (defconstant ASSERT_MY_PARENT_ID 71) + (defconstant ASSERT_MY_PUZZLEHASH 72) + (defconstant ASSERT_MY_AMOUNT 73) + + ; the conditions below ensure that we're "far enough" in the future + + ; wall-clock time + (defconstant ASSERT_SECONDS_RELATIVE 80) + (defconstant ASSERT_SECONDS_ABSOLUTE 81) + + ; block index + (defconstant ASSERT_HEIGHT_RELATIVE 82) + (defconstant ASSERT_HEIGHT_ABSOLUTE 83) + + ; A condition that is always true and always ignore all arguments + (defconstant REMARK 1) +) diff --git a/resources/tests/game-referee-in-cl21/curry-and-treehash.clinc b/resources/tests/game-referee-in-cl21/curry-and-treehash.clinc new file mode 100644 index 000000000..6a63e9364 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/curry-and-treehash.clinc @@ -0,0 +1,92 @@ +( + ;; The code below is used to calculate of the tree hash of a curried function + ;; without actually doing the curry, and using other optimization tricks + ;; like unrolling `shatree`. + + (defconstant ONE 1) + (defconstant TWO 2) + (defconstant A_KW #a) + (defconstant Q_KW #q) + (defconstant C_KW #c) + + ;; Given the tree hash `environment-hash` of an environment tree E + ;; and the tree hash `parameter-hash` of a constant parameter P + ;; return the tree hash of the tree corresponding to + ;; `(c (q . P) E)` + ;; This is the new environment tree with the addition parameter P curried in. + ;; + ;; Note that `(c (q . P) E)` = `(c . ((q . P) . (E . 0)))` + + (defun-inline update-hash-for-parameter-hash (parameter-hash environment-hash) + (sha256 TWO (sha256 ONE C_KW) + (sha256 TWO (sha256 TWO (sha256 ONE Q_KW) parameter-hash) + (sha256 TWO environment-hash (sha256 ONE 0)))) + ) + + ;; This function recursively calls `update-hash-for-parameter-hash`, updating `environment-hash` + ;; along the way. + + (defun build-curry-list (reversed-curry-parameter-hashes environment-hash) + (if reversed-curry-parameter-hashes + (build-curry-list (r reversed-curry-parameter-hashes) + (update-hash-for-parameter-hash (f reversed-curry-parameter-hashes) environment-hash)) + environment-hash + ) + ) + + ;; Given the tree hash `environment-hash` of an environment tree E + ;; and the tree hash `function-hash` of a function tree F + ;; return the tree hash of the tree corresponding to + ;; `(a (q . F) E)` + ;; This is the hash of a new function that adopts the new environment E. + ;; This is used to build of the tree hash of a curried function. + ;; + ;; Note that `(a (q . F) E)` = `(a . ((q . F) . (E . 0)))` + + (defun-inline tree-hash-of-apply (function-hash environment-hash) + (sha256 TWO (sha256 ONE A_KW) + (sha256 TWO (sha256 TWO (sha256 ONE Q_KW) function-hash) + (sha256 TWO environment-hash (sha256 ONE 0)))) + ) + + ;; function-hash: + ;; the hash of a puzzle function, ie. a `mod` + ;; + ;; reversed-curry-parameter-hashes: + ;; a list of pre-hashed trees representing parameters to be curried into the puzzle. + ;; Note that this must be applied in REVERSED order. This may seem strange, but it greatly simplifies + ;; the underlying code, since we calculate the tree hash from the bottom nodes up, and the last + ;; parameters curried must have their hashes calculated first. + ;; + ;; we return the hash of the curried expression + ;; (a (q . function-hash) (c (cp1 (c cp2 (c ... 1)...)))) + ;; + ;; Note that from a user's perspective the hashes passed in here aren't simply + ;; the hashes of the desired parameters, but their treehash representation since + ;; that's the form we're assuming they take in the actual curried program. + + (defun puzzle-hash-of-curried-function (function-hash . reversed-curry-parameter-hashes) + (tree-hash-of-apply function-hash + (build-curry-list reversed-curry-parameter-hashes (sha256 ONE ONE))) + ) + + (defconstant b32 32) + + (defun-inline size_b32 (var) + (= (strlen var) b32) + ) + + (defun calculate_coin_id (parent puzzlehash amount) + (if (all (size_b32 parent) (size_b32 puzzlehash) (> amount -1)) + (sha256 parent puzzlehash amount) + (x) + ) + ) + + ; takes a lisp tree and returns the hash of it + (defun shatree (TREE) + (if (l TREE) + (sha256 2 (shatree (f TREE)) (shatree (r TREE))) + (sha256 1 TREE))) + +) diff --git a/resources/tests/game-referee-in-cl21/curry.clinc b/resources/tests/game-referee-in-cl21/curry.clinc new file mode 100644 index 000000000..81a1ec3a6 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/curry.clinc @@ -0,0 +1,104 @@ +( + ;; The code below is used to calculate of the tree hash of a curried function + ;; without actually doing the curry, and using other optimization tricks + ;; like unrolling `shatree`. + + (defconstant TWO 2) + (defconstant constant-tree ( + (0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a . ; = `(sha256 1)` + 0x9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2) . ; = `(sha256 1 1)` = `(sha256 1 #q)` + (0x02a12871fee210fb8619291eaea194581cbd2531e4b23759d225f6806923f63222 . ; = `(concat 2 (sha256 1 #a))` + 0x02a8d5dd63fba471ebcb1f3e8f7c1e1879b7152a6e7298a91ce119a63400ade7c5) ; = `(concat 2 (sha256 1 #c))` + ) + ) + + ; I looked into calculating the values of `constant-tree` because it's pretty easy to code-golf + ; out an implementation that produces the values cheaper than just inlining them. The problem is, + ; when do we calculate them? If there were a way to calculate it "before main" and include it in + ; the globally-accessible constant table, we could do that. But we can't which means to be optimal, + ; client code should call the "build table" code once, then pass it around to anyone that wants to + ; call `curry` or `curry2`. This is pretty intrusive, so for now we'll just use the existing + ; global constant infrastructure, and include it as a fixed table so the tree of four values will + ; appear in all code that includes this file, and it will compress better in generators. + + (defun-inline sha256_one _noargs (f (f constant-tree))) + (defun-inline sha256_one_one _noargs (r (f constant-tree))) + (defun-inline two_sha256_one_a_kw _noargs (f (r constant-tree))) + (defun-inline two_sha256_one_c_kw _noargs (r (r constant-tree))) + + ;; this returns the sha256 tree hash of expression F = `((q . a1) a2)` + (defun hash-expression-F (a1 a2) + (sha256 TWO (sha256 TWO (sha256_one_one) a1) + (sha256 TWO a2 (sha256_one))) + ) + + ;; Given the tree hash `environment-hash` of an environment tree E + ;; and the tree hash `parameter-hash` of a constant parameter P + ;; return the tree hash of the tree corresponding to + ;; `(c (q . P) E)` + ;; This is the new environment tree with the addition parameter P curried in. + ;; + ;; Note that `(c (q . P) E)` = `(c . ((q . P) . (E . 0)))` + + (defun-inline update-hash-for-parameter-hash (parameter-hash environment-hash) + (sha256 (two_sha256_one_c_kw) (hash-expression-F parameter-hash environment-hash)) + ) + + ;; Given the tree hash `environment-hash` of an environment tree E + ;; and the tree hash `mod-hash` of a mod M + ;; return the tree hash of the tree corresponding to + ;; `(a (q . M) E)` + ;; This is the hash of a new function that adopts the new environment E. + ;; This is used to build of the tree hash of a curried function. + ;; + ;; Note that `(a (q . M) E)` = `(a . ((q . M) . (E . 0)))` + + (defun-inline tree-hash-of-apply (mod-hash environment-hash) + (sha256 (two_sha256_one_a_kw) (hash-expression-F mod-hash environment-hash)) + ) + + ;; This function recursively calls `update-hash-for-parameter-hash` + + (defun calculate-hash-of-curried-parameters (curry-parameter-hashes) + (if curry-parameter-hashes + (update-hash-for-parameter-hash (f curry-parameter-hashes) (calculate-hash-of-curried-parameters (r curry-parameter-hashes))) + (sha256_one_one) + ) + ) + + ;; mod-hash: + ;; the hash of a puzzle function, ie. a `mod` + ;; + ;; curry-parameter-hashes: + ;; a list of pre-hashed trees representing parameters to be curried into the puzzle. + ;; + ;; we return the hash of the curried expression + ;; (a (q . mod-hash) (c (cp1 (c cp2 (c ... 1)...)))) + ;; + ;; Note that from a user's perspective the hashes passed in here aren't simply + ;; the hashes of the desired parameters, but their treehash representation since + ;; that's the form we're assuming they take in the acutal curried program. + + ;; inline functions that take varargs don't seem to work, so we can't inline `curry` + + (defun curry_hashes (mod-hash . curry-parameter-hashes) + (tree-hash-of-apply mod-hash + (calculate-hash-of-curried-parameters curry-parameter-hashes)) + ) + + + ;; this is included for future compilers that handle it properly. If you get weird + ;; errors using this, it may be your tooling. Use `curry` above instead, or inline manually. + + (defun-inline curry_hashes_inline (mod-hash . curry-parameter-hashes) + (tree-hash-of-apply mod-hash + (calculate-hash-of-curried-parameters curry-parameter-hashes)) + ) + + ;; `curry_mod_hashes_inline` takes exactly two parameters rather than varags, and it can be inlined + + (defun-inline curry_mod_hashes_inline (mod-hash curry-parameter-hashes) + (tree-hash-of-apply mod-hash + (calculate-hash-of-curried-parameters curry-parameter-hashes)) + ) +) diff --git a/resources/tests/game-referee-in-cl21/deep_compare.clinc b/resources/tests/game-referee-in-cl21/deep_compare.clinc new file mode 100644 index 000000000..0a863ae33 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/deep_compare.clinc @@ -0,0 +1,41 @@ + +( + (defun deep_compare (a b) + (if (l a) + (if (l b) + (assign-lambda inner_result (deep_compare (f a) (f b)) + (if inner_result + inner_result + (deep_compare (r a) (r b)) + ) + ) + 1 + ) + (if (l b) + -1 + (if (> a b) + 1 + (- 0 (> b a)) + ) + ) + ) + ) + (defun deep< (a b) + (= (deep_compare a b) -1) + ) + (defun deep> (a b) + (= (deep_compare a b) 1) + ) + (defun deep= (a b) + (= (deep_compare a b) 0) + ) + (defun deep<= (a b) + (not (deep> a b)) + ) + (defun deep>= (a b) + (not (deep< a b)) + ) + (defun deep!= (a b) + (not (deep= a b)) + ) +) diff --git a/resources/tests/game-referee-in-cl21/filtermap.clinc b/resources/tests/game-referee-in-cl21/filtermap.clinc new file mode 100644 index 000000000..59c827858 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/filtermap.clinc @@ -0,0 +1,14 @@ + +( + (defun filtermap (process remaining init) + (if remaining + (assign next (a process (list (f remaining))) + (if next + (c next (filtermap process (r remaining) init)) + (filtermap process (r remaining) init) + ) + ) + init + ) + ) +) \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/flatten.clinc b/resources/tests/game-referee-in-cl21/flatten.clinc new file mode 100644 index 000000000..ab3815abd --- /dev/null +++ b/resources/tests/game-referee-in-cl21/flatten.clinc @@ -0,0 +1,12 @@ +( + (defun flatten_list (everything) + (if + (not everything) 0 + (prepend (f everything) (flatten_list (r everything))) + ) + ) + (defun flatten everything + (flatten_list everything) + ) + +) \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/handcalc.clinc b/resources/tests/game-referee-in-cl21/handcalc.clinc new file mode 100644 index 000000000..6317733f4 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/handcalc.clinc @@ -0,0 +1,140 @@ + +; ranks are 2-14 with 2 being two, 13 being king, and 14 being ace +; suits are 1-4 with no particular labelling +; takes a list of cards (rank . suit) and returns the value of the best poker +; hand which can be made with them +; returned list is hand type followed by cards in descending order +; doesn't work for ten or more cards if there are multiple flushes +; all sorting is done highest to lowest +( + (defconstant STRAIGHT_FLUSH 9) + (defconstant FOUR_OF_A_KIND 8) + (defconstant FULL_HOUSE 7) + (defconstant FLUSH 6) + (defconstant STRAIGHT 5) + (defconstant THREE_OF_A_KIND 4) + (defconstant TWO_PAIR 3) + (defconstant PAIR 2) + (defconstant HIGH_CARD 1) + (defun find_flush_inner (suits last count) + (if (not suits) + 0 + (if (= (f suits) last) + (if (= count 4) + last + (find_flush_inner (r suits) last (+ count 1)) + ) + (find_flush_inner (r suits) (f suits) 1) + ) + ) + ) + ; returns the flush suit or 0 if there isn't any + ; suits must be clustered/sorted + (defun find_flush (suits) + (find_flush_inner (sort (lambda (x y) (deep> x y)) suits) 0 0) + ) + (defun straight_high_inner (ranks started_ace last count) + (if (not ranks) + ; at the end of the list + (if (logand (= last 2) (= count 4) started_ace) + ; ace to five + 5 + ; no straight + 0 + ) + (if (= last (f ranks)) + ; skip identical cards + (straight_high_inner (r ranks) started_ace last count) + ; if the partial straight continues + (if (= (f ranks) (- last 1)) + (if (= count 4) + ; found a straight, add 3 to last because next and last are included + (+ last 3) + ; keep looking for a straight with the count going up by one + (straight_high_inner (r ranks) started_ace (f ranks) (+ count 1)) + ) + ; reset the count + (straight_high_inner (r ranks) started_ace (f ranks) 1) + ) + ) + ) + ) + ; returns the high card of a straight or 0 if there isn't any + ; ranks must be sorted in descending order + (defun straight_high (ranks) + (straight_high_inner ranks (= (f ranks) 14) 0 0) + ) + (defun group_by_count_inner (items last count) + (if (not items) + (list (c count last)) + (if (= (f items) last) + (group_by_count_inner (r items) last (+ count 1)) + (assign val (group_by_count_inner (r items) (f items) 1) + (c (c count last) val) + ) + ) + ) + ) + (defun group_by_count (items) + (group_by_count_inner items (f items) 0) + ) + (defun handcalc (cards) + (assign-lambda + first (lambda (x) (f x)) + rest (lambda (x) (r x)) + fsuit (find_flush (map rest cards)) + max_flush (if (not fsuit) + 0 + (assign-lambda + fnosuits + (sort + (lambda (x y) (deep> x y)) + (filtermap + (lambda ((& fsuit) (card_rank . card_suit)) + (if (= fsuit card_suit) + card_rank + 0 + ) + ) + cards + 0 + ) + ) + + fsh (straight_high fnosuits) + (if fsh + (list STRAIGHT_FLUSH fsh) + (c FLUSH (slice fnosuits 5)) + ) + ) + ) + nosuits (sort (lambda (x y) (deep> x y)) (map first cards)) + sh (straight_high nosuits) + max_straight (if sh + (list STRAIGHT sh) + 0 + ) + groups (sort (lambda (x y) (deep> x y)) (group_by_count nosuits)) + (top_count . top_card) (f groups) + (second_count . second_card) (f (r groups)) + topcards (map rest groups) + max_group (if (= top_count 1) + (c HIGH_CARD (slice topcards 5)) + (if (= top_count 2) + (if (= second_count 1) + (c PAIR (slice topcards 4)) + (c TWO_PAIR (slice topcards 3)) + ) + (if (= top_count 3) + (if (= second_count 1) + (c THREE_OF_A_KIND (slice topcards 3)) + (c FULL_HOUSE (slice topcards 2)) + ) + (c FOUR_OF_A_KIND (slice topcards 2)) + ) + ) + ) + (max (lambda (x y) (deep< x y)) (list max_flush max_straight max_group)) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/handcalc_a.clinc b/resources/tests/game-referee-in-cl21/handcalc_a.clinc new file mode 100644 index 000000000..06c326392 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/handcalc_a.clinc @@ -0,0 +1,256 @@ + +; ranks are 2-14 with 2 being two, 13 being king, and 14 being ace +; suits are 1-4 with no particular labelling +; takes a list of cards (rank . suit) and returns the value of the best poker +; hand which can be made with them +; returned list is hand type followed by cards in descending order +; doesn't work for ten or more cards if there are multiple flushes +; all sorting is done highest to lowest +( + (defconstant STRAIGHT_FLUSH 9) + (defconstant FOUR_OF_A_KIND 8) + (defconstant FULL_HOUSE 7) + (defconstant FLUSH 6) + (defconstant STRAIGHT 5) + (defconstant THREE_OF_A_KIND 4) + (defconstant TWO_PAIR 3) + (defconstant PAIR 2) + (defconstant HIGH_CARD 1) + (defun hand_compare (a b) + (if (= (f a) (f b)) + (if (r a) + (hand_compare (r a) (r b)) + 0 + ) + (- (* 2 (> (f a) (f b))) 1) + ) + ) + (defun hand< (a b) + (= (hand_compare a b) -1) + ) + (defun merge (a b) + (if (not a) + b + (if (not b) + a + (if (> (f a) (f b)) + (c (f a) (merge (r a) b)) + (c (f b) (merge a (r b))) + ) + ) + ) + ) + ; Sorts atoms into descending order + ; This is optimized for sorting short lists + ; A more general function would return a list of lists of ascending sizes + ; to be merged + (defun atomsort ((@ firstpos (first @ secondpos (second @ thirdpos (third . remaining))))) + (if firstpos + (if secondpos + (if thirdpos + (assign-lambda + mylist + (if (> first second) + (if (> second third) + (list first second third) + (if (> first third) + (list first third second) + (list third first second) + ) + ) + (if (> first third) + (list second first third) + (if (> second third) + (list second third first) + (list third second first) + ) + ) + ) + (merge mylist (atomsort remaining)) + ) + (if (> first second) + firstpos + (list second first) + ) + ) + firstpos + ) + 0 + ) + ) + (defun count_suits_2 ((@ suits (firstsuit . remaining))) + (if (not suits) + (c (c 0 0) (c 0 0)) + (assign-lambda ((@ cd (clubs . diamonds)) . (@ hs (hearts . spades))) (count_suits remaining) + (if (= firstsuit 1) + (c (c (+ clubs 1) diamonds) hs) + (if (= firstsuit 2) + (c (c clubs (+ diamonds 1)) hs) + (if (= firstsuit 3) + (c cd (c (+ hearts 1) spades)) + (c cd (c hearts (+ spades 1))) + ) + ) + ) + ) + ) + ) + (defun find_flush_2 (suits) + (assign-lambda ((clubs . diamonds) . (hearts . spades)) (count_suits suits) + (if (> clubs 4) + 1 + (if (> diamonds 4) + 2 + (if (> hearts 4) + 3 + (if (> spades 4) + 4 + 0 + ) + ) + ) + ) + ) + ) + (defun count_suits (suits) + (if suits + (+ (count_suits (r suits)) (ash 1 (* 4 (- (f suits) 1)))) + 0 + ) + ) + (defun find_flush (suits) + (assign-lambda + myvals (count_suits suits) + (if (> (logand myvals 0xF000) 0x4000) + 4 + (if (> (logand myvals 0x0F00) 0x0400) + 3 + (if (> (logand myvals 0x00F0) 0x0040) + 2 + (if (> (logand myvals 0x0F) 0x04) + 1 + 0 + ) + ) + ) + ) + ) + ) + (defun straight_high_inner (ranks last count) + (if (not ranks) + (if (logand (= last 2) (= count 4)) + ; maybe ace to five + 5 + 0 + ) + (if (= last (f ranks)) + ; skip identical cards + (straight_high_inner (r ranks) last count) + ; if the partial straight continues + (if (= (f ranks) (- last 1)) + (if (= count 4) + ; found a straight, add 3 to last because next and last are included + (+ last 3) + ; keep looking for a straight with the count going up by one + (straight_high_inner (r ranks) (f ranks) (+ count 1)) + ) + ; reset the count + (straight_high_inner (r ranks) (f ranks) 1) + ) + ) + ) + ) + ; returns the high card of a straight or 0 if there isn't any + ; ranks must be sorted in descending order + (defun straight_high (ranks) + (assign-lambda high (straight_high_inner ranks 0 0) + (if (= high 5) + (* (= (f ranks) 14) 5) + high + ) + ) + ) + (defun group_by_count_inner (items last count) + (if (not items) + (list (logior (lsh count 4) last)) + (if (= (f items) last) + (group_by_count_inner (r items) last (+ count 1)) + (assign-inline val (group_by_count_inner (r items) (f items) 1) + (c (logior (lsh count 4) last) val) + ) + ) + ) + ) + (defun group_by_count (items) + (group_by_count_inner items (f items) 0) + ) + (defun handcalc (cards) + (assign-lambda + first (lambda (x) (f x)) + rest (lambda (x) (r x)) + fsuit (find_flush (map rest cards)) + max_flush (if (not fsuit) + (list 0) + (assign-lambda + flushcards + (filtermap + (lambda ((& fsuit) (card_rank . card_suit)) + (if (= fsuit card_suit) + card_rank + 0 + ) + ) + cards + 0 + ) + flushranks (atomsort flushcards) + fsh (straight_high flushranks) + (if fsh + (list STRAIGHT_FLUSH fsh) + (c FLUSH (slice flushranks 5)) + ) + ) + ) + ranks (atomsort (map first cards)) + sh (straight_high ranks) + max_straight (if sh + (list STRAIGHT sh) + (list 0) + ) + groups (map + (lambda (myval) (c (lsh myval -4) (logand myval 0x0F))) + (atomsort (group_by_count ranks)) + ) + (top_count . top_card) (f groups) + (second_count . second_card) (f (r groups)) + topcards (map rest groups) + max_group (if (= top_count 1) + (c HIGH_CARD (slice topcards 5)) + (if (= top_count 2) + (if (= second_count 1) + (c PAIR (slice topcards 4)) + (c TWO_PAIR (slice topcards 3)) + ) + (if (= top_count 3) + (if (= second_count 1) + (c THREE_OF_A_KIND (slice topcards 3)) + (c FULL_HOUSE (slice topcards 2)) + ) + (c FOUR_OF_A_KIND (slice topcards 2)) + ) + ) + ) + ; max of max_flush max_straight and max_group + (if (> (f max_flush) (f max_straight)) + (if (> (f max_flush) (f max_group)) + max_flush + max_group + ) + (if (> (f max_straight) (f max_group)) + max_straight + max_group + ) + ) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/last.clinc b/resources/tests/game-referee-in-cl21/last.clinc new file mode 100644 index 000000000..5a6ffd73e --- /dev/null +++ b/resources/tests/game-referee-in-cl21/last.clinc @@ -0,0 +1,39 @@ +( + (defun last_inner ((next . remainder)) + (if remainder + (last_inner remainder) + next + ) + ) + + (defmacro last ARGS + (defun snoc (L agg) + (if L + (if (r L) + (snoc (r L) (c (f L) agg)) + (c (f L) agg) + ) + (c () ()) + ) + ) + + (defun prefix (L P) + (if L + (c (f L) (prefix (r L) P)) + P + ) + ) + + (if ARGS + (if (r ARGS) + (assign + (final . rest) (snoc ARGS ()) + reversed (prefix rest (list final)) + (qq (last_inner (unquote (c list reversed)))) + ) + (qq (last_inner (unquote (f ARGS)))) + ) + (x "Last takes at least one argument") + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/len.clinc b/resources/tests/game-referee-in-cl21/len.clinc new file mode 100644 index 000000000..407c36694 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/len.clinc @@ -0,0 +1,3 @@ +( + (defun len (L) (if L (+ 1 (len (r L))) 0)) +) diff --git a/resources/tests/game-referee-in-cl21/map.clinc b/resources/tests/game-referee-in-cl21/map.clinc new file mode 100644 index 000000000..016c3a0e4 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/map.clinc @@ -0,0 +1,17 @@ +( + (defun map-with-rest (F L R) + (if L + (c (a F (list (f L))) (map-with-rest F (r L) R)) + R + ) + ) + + (defmacro map ARGS + (defun list-len (X) (if X (+ 1 (list-len (r X))) 0)) + + (if (= (list-len ARGS) 3) + (qq (map-with-rest (unquote (f ARGS)) (unquote (f (r ARGS))) (unquote (f (r (r ARGS)))))) + (qq (map-with-rest (unquote (f ARGS)) (unquote (f (r ARGS))) ())) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/match.clinc b/resources/tests/game-referee-in-cl21/match.clinc new file mode 100644 index 000000000..d66593c55 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/match.clinc @@ -0,0 +1,12 @@ + +( + (defun match (process remaining) + (if remaining + (if (a process (list (f remaining))) + (f remaining) + (match process (r remaining)) + ) + 0 + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/max.clinc b/resources/tests/game-referee-in-cl21/max.clinc new file mode 100644 index 000000000..5ec6d54f6 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/max.clinc @@ -0,0 +1,13 @@ +( + (defun max_inner (myless best_so_far mylist) + (if (not mylist) best_so_far + (if (a myless (list best_so_far (f mylist))) + (max_inner myless (f mylist) (r mylist)) + (max_inner myless best_so_far (r mylist)) + ) + ) + ) + (defun max (myless mylist) + (max_inner myless (f mylist) (r mylist)) + ) +) diff --git a/resources/tests/game-referee-in-cl21/permutations.clinc b/resources/tests/game-referee-in-cl21/permutations.clinc new file mode 100644 index 000000000..9664c6aff --- /dev/null +++ b/resources/tests/game-referee-in-cl21/permutations.clinc @@ -0,0 +1,21 @@ +( + (defun permutations_inner (pre post agg) + (if (not post) + agg + (assign + myatom (f post) + newrest (r post) + (map (lambda ((& myatom) x) (c myatom x)) + (permutations (prepend pre newrest)) + (permutations_inner (c myatom pre) newrest agg) + ) + ) + ) + ) + (defun permutations (vals) + (if vals + (permutations_inner 0 vals 0) + (q ()) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/prefix.clinc b/resources/tests/game-referee-in-cl21/prefix.clinc new file mode 100644 index 000000000..641723dad --- /dev/null +++ b/resources/tests/game-referee-in-cl21/prefix.clinc @@ -0,0 +1,16 @@ +( + (defmacro prefix ARGS + (defun compile-list (args) + (if args + (if (r args) + ;; We have at least 2 things left... recurse once. + (qq (c (unquote (f args)) (unquote (compile-list (r args))))) + ;; This is the last item, so we return it whole (improper list form). + (qq (unquote (f args))) + ) + 0 + ) + ) + (compile-list ARGS) + ) +) \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/prepend.clinc b/resources/tests/game-referee-in-cl21/prepend.clinc new file mode 100644 index 000000000..2ea293409 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/prepend.clinc @@ -0,0 +1,8 @@ +( + (defun prepend (a b) + (if a + (c (f a) (prepend (r a) b)) + b + ) + ) +) \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/print.clinc b/resources/tests/game-referee-in-cl21/print.clinc new file mode 100644 index 000000000..95d459b36 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/print.clinc @@ -0,0 +1,3 @@ +( + (defun print (R P) (if (all "$print$" R P) P P)) +) diff --git a/resources/tests/game-referee-in-cl21/range.clinc b/resources/tests/game-referee-in-cl21/range.clinc new file mode 100644 index 000000000..dc1e61ca3 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/range.clinc @@ -0,0 +1,11 @@ +( + (defun range_inner (next final) + (if (= next final) + 0 + (c next (range_inner (+ next 1) final)) + ) + ) + (defun range (i) + (range_inner 0 i) + ) +) \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/reduce.clinc b/resources/tests/game-referee-in-cl21/reduce.clinc new file mode 100644 index 000000000..3251c79a7 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/reduce.clinc @@ -0,0 +1,10 @@ + +( + ; From here to the meat should be in a standard library + (defun reduce (fun lst init) + (if lst + (reduce fun (r lst) (a fun (list (f lst) init))) + init + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/reverse.clinc b/resources/tests/game-referee-in-cl21/reverse.clinc new file mode 100644 index 000000000..389963ee9 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/reverse.clinc @@ -0,0 +1,11 @@ +( + (defun reverse_inner (reversed rest) + (if rest + (reverse_inner (c (f rest) reversed) (r rest)) + reversed + ) + ) + (defun reverse (vals) + (reverse_inner 0 vals) + ) +) diff --git a/resources/tests/game-referee-in-cl21/shatree.clinc b/resources/tests/game-referee-in-cl21/shatree.clinc new file mode 100644 index 000000000..05bdb2699 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/shatree.clinc @@ -0,0 +1,11 @@ +( + ;; hash a tree + ;; This is used to calculate a puzzle hash given a puzzle program. + (defun shatree + (TREE) + (if (l TREE) + (sha256 2 (shatree (f TREE)) (shatree (r TREE))) + (sha256 1 TREE) + ) + ) +) \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/slice.clinc b/resources/tests/game-referee-in-cl21/slice.clinc new file mode 100644 index 000000000..2c98a09c5 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/slice.clinc @@ -0,0 +1,10 @@ + +( + ; returns the first count elements of mylist + (defun slice (mylist count) + (if (print (list "slice inputs" nylist count) (not count)) + 0 + (c (f mylist) (slice (r mylist) (- count 1))) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clsp b/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clsp new file mode 100644 index 000000000..8df629518 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clsp @@ -0,0 +1,28 @@ +(mod () + (include *standard-cl-21*) + (include print.clinc) + (include prepend.clinc) + (include sort.clinc) + (include assert.clinc) + (include map.clinc) + (include deep_compare.clinc) + + (map + (lambda ((want_cmp_val cmp_a cmp_b)) + (= (deep_compare cmp_a cmp_b) want_cmp_val) + ) + (q + (0 0 0) + (-1 () (1 14 5 4 3 2)) + (1 (1 14 5 4 3 2) ()) + (-1 "X" (1 2)) + (1 (1 2) "X") + (0 (3 2) (3 2)) + (-1 (3 1) (3 3)) + (1 (3 3) (3 1)) + (-1 (1 1) (2 1)) + (1 (3 1) (2 2)) + (-1 (2 2) (3 1)) + ) + ) + ) diff --git a/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clvm.hex.reference b/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clvm.hex.reference new file mode 100644 index 000000000..cb3eb0bf7 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff08ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff0e80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ffff01ffff80ff80ff8080ffff81ffff80ffff01ff0eff05ff04ff03ff028080ffff01ffff01ff0eff05ff04ff03ff0280ff8080ffff81ffff58ffff01ff028080ffff01ffff01ff0280ff5880ffff80ffff03ff0280ffff03ff028080ffff81ffffff03ff0180ffff03ff038080ffff01ffff03ff0380ffff03ff018080ffff81ffffff01ff0180ffff02ff018080ffff01ffff03ff0180ffff02ff028080ffff81ffffff02ff0280ffff03ff01808080ffff04ffff0180ff808080808080ffff04ffff01ffffff02ffff03ff0bffff01ff02ffff01ff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff02ff08ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff80808080808080ff0180ffff01ff02ffff0117ff018080ff0180ff02ffff03ffff07ff0580ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff02ff0affff04ff02ffff04ffff06ff0180ffff04ffff02ff0cffff04ff02ffff04ffff05ff0580ffff04ffff05ff0b80ff8080808080ff8080808080ff0180ffff01ff02ffff01ff0101ff018080ff0180ff0180ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff0181ffff0180ffff01ff02ffff01ff02ffff03ffff15ff05ff0b80ffff01ff02ffff01ff0101ff0180ffff01ff02ffff01ff11ffff0180ffff15ff0bff058080ff018080ff0180ff018080ff0180ff018080ff0180ffff02ffff03ff0bffff01ff02ffff010bff0180ffff01ff02ffff01ff02ff0cffff04ff02ffff04ffff06ff0980ffff04ffff06ff1580ff8080808080ff018080ff0180ff09ffff02ff0cffff04ff02ffff04ff2bffff04ff5bff8080808080ff1380ff018080 diff --git a/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.sym b/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.sym new file mode 100644 index 000000000..3d2e104b3 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.sym @@ -0,0 +1 @@ +{"026e28eecfeb07594300bd6c3bce34bd4fe340a189ddd5312c7361e242b52160_arguments": "(() (want_cmp_val cmp_a cmp_b))", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/smoke_test_deep_compare.clsp", "__chia__main_arguments": "()", "4fd7e0dfbd7ac5775e6d61670f548dc35ff62a1a4b14f2f3efe09d8b39e6988b_left_env": "1", "a08c534dd3a89e22a8fadaa027c6b9a8dd4f9a00b72c315821703d8ee1c8f687": "deep_compare", "026e28eecfeb07594300bd6c3bce34bd4fe340a189ddd5312c7361e242b52160_left_env": "1", "a08c534dd3a89e22a8fadaa027c6b9a8dd4f9a00b72c315821703d8ee1c8f687_arguments": "(a b)", "d9f6d93919b2fb79bc296ac80d73b5c48bf044e457f0f678b210f990efeea743": "letbinding_$_58", "d9f6d93919b2fb79bc296ac80d73b5c48bf044e457f0f678b210f990efeea743_left_env": "1", "a08c534dd3a89e22a8fadaa027c6b9a8dd4f9a00b72c315821703d8ee1c8f687_left_env": "1", "4fd7e0dfbd7ac5775e6d61670f548dc35ff62a1a4b14f2f3efe09d8b39e6988b": "map-with-rest", "4fd7e0dfbd7ac5775e6d61670f548dc35ff62a1a4b14f2f3efe09d8b39e6988b_arguments": "(F L R)", "d9f6d93919b2fb79bc296ac80d73b5c48bf044e457f0f678b210f990efeea743_arguments": "((a_$_43 b_$_44) inner_result)", "026e28eecfeb07594300bd6c3bce34bd4fe340a189ddd5312c7361e242b52160": "lambda_$_57"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/smoke_test_permutations.clsp b/resources/tests/game-referee-in-cl21/smoke_test_permutations.clsp new file mode 100644 index 000000000..2ed3fbbcc --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_permutations.clsp @@ -0,0 +1,9 @@ +(mod (X) + (include *standard-cl-21*) + (include map.clinc) + (include prepend.clinc) + (include print.clinc) + (include permutations.clinc) + + (permutations X) + ) diff --git a/resources/tests/game-referee-in-cl21/smoke_test_permutations.clvm.hex.reference b/resources/tests/game-referee-in-cl21/smoke_test_permutations.clvm.hex.reference new file mode 100644 index 000000000..ad4e2fba6 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_permutations.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff1effff04ff02ffff04ff05ff80808080ffff04ffff01ffffff02ffff03ff0bffff01ff02ffff01ff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff02ff08ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff80808080808080ff0180ffff01ff02ffff0117ff018080ff0180ffff02ffff03ff05ffff01ff02ffff01ff04ffff05ff0580ffff02ff14ffff04ff02ffff04ffff06ff0580ffff04ff0bff808080808080ff0180ffff01ff02ffff010bff018080ff0180ff02ffff03ffff20ff0b80ffff01ff02ffff0117ff0180ffff01ff02ffff01ff02ff0affff04ff02ffff04ffff06ff0180ffff04ffff05ff0b80ffff04ffff06ff0b80ff808080808080ff018080ff0180ffff02ff08ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff1680ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff04ff0bff808080ffff01ff01808080ff80808080ffff04ffff02ff1effff04ff02ffff04ffff02ff14ffff04ff02ffff04ff09ffff04ff17ff8080808080ff80808080ffff04ffff02ff1cffff04ff02ffff04ffff04ff0bff0980ffff04ff17ffff04ff2dff808080808080ff808080808080ffff04ff09ff0b80ff02ffff03ff05ffff01ff02ffff01ff02ff1cffff04ff02ffff04ffff0180ffff04ff05ffff04ffff0180ff808080808080ff0180ffff01ff02ffff01ff01ff8080ff018080ff0180ff018080 diff --git a/resources/tests/game-referee-in-cl21/smoke_test_permutations.sym b/resources/tests/game-referee-in-cl21/smoke_test_permutations.sym new file mode 100644 index 000000000..c53c294de --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_permutations.sym @@ -0,0 +1 @@ +{"0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708_arguments": "((myatom) x)", "4fd7e0dfbd7ac5775e6d61670f548dc35ff62a1a4b14f2f3efe09d8b39e6988b": "map-with-rest", "cbc879391ee3747e5a0a6457916b75a38118f0febf74e14983e66c8105ffb9c8": "permutations_inner", "f6344fd0045418e460a57d88fc880464d12fcafb3b3c3adbc52e0e39672bf0c8": "letbinding_$_387", "e209270f1dc5e9d4b9d3c077d401c47f47bb866e87d1707551d101b8717f4edf_arguments": "(vals)", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/smoke_test_permutations.clsp", "cbc879391ee3747e5a0a6457916b75a38118f0febf74e14983e66c8105ffb9c8_arguments": "(pre post agg)", "048d5413ce5265ab36813735bc75ba7b7c5e9454553198bfdafef009677e420b_arguments": "(a b)", "e209270f1dc5e9d4b9d3c077d401c47f47bb866e87d1707551d101b8717f4edf_left_env": "1", "4fd7e0dfbd7ac5775e6d61670f548dc35ff62a1a4b14f2f3efe09d8b39e6988b_arguments": "(F L R)", "4fd7e0dfbd7ac5775e6d61670f548dc35ff62a1a4b14f2f3efe09d8b39e6988b_left_env": "1", "048d5413ce5265ab36813735bc75ba7b7c5e9454553198bfdafef009677e420b": "prepend", "cbc879391ee3747e5a0a6457916b75a38118f0febf74e14983e66c8105ffb9c8_left_env": "1", "__chia__main_arguments": "(X)", "e209270f1dc5e9d4b9d3c077d401c47f47bb866e87d1707551d101b8717f4edf": "permutations", "f6344fd0045418e460a57d88fc880464d12fcafb3b3c3adbc52e0e39672bf0c8_left_env": "1", "f6344fd0045418e460a57d88fc880464d12fcafb3b3c3adbc52e0e39672bf0c8_arguments": "((pre_$_383 post_$_384 agg_$_385) myatom newrest)", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708": "lambda_$_388", "048d5413ce5265ab36813735bc75ba7b7c5e9454553198bfdafef009677e420b_left_env": "1", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708_left_env": "1"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/smoke_test_sort.clsp b/resources/tests/game-referee-in-cl21/smoke_test_sort.clsp new file mode 100644 index 000000000..59b8a886e --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_sort.clsp @@ -0,0 +1,11 @@ +(mod (X) + (include *standard-cl-21*) + (include prepend.clinc) + (include sort.clinc) + (include assert.clinc) + (include map.clinc) + (include reverse.clinc) + (include print.clinc) + + (sort (lambda (a b) (> b a)) X) + ) diff --git a/resources/tests/game-referee-in-cl21/smoke_test_sort.clvm.hex.reference b/resources/tests/game-referee-in-cl21/smoke_test_sort.clvm.hex.reference new file mode 100644 index 000000000..476b5f74e --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_sort.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff12ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff3e80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ff05ff8080808080ffff04ffff01ffffffff02ffff03ff05ffff01ff02ffff01ff04ffff05ff0580ffff02ff10ffff04ff02ffff04ffff06ff0580ffff04ff0bff808080808080ff0180ffff01ff02ffff010bff018080ff0180ff02ffff03ff05ffff01ff02ffff01ff02ff18ffff04ff02ffff04ffff06ff0580ffff04ff17ffff04ffff04ffff05ff0580ff0b80ff808080808080ff0180ffff01ff02ffff01ff06ff0380ff018080ff0180ffff02ff18ffff04ff02ffff04ff05ffff01ff80ff8080808080ffff02ffff03ffff20ff0b80ffff01ff02ffff01ff02ff10ffff04ff02ffff04ffff02ff2effff04ff02ffff04ff2fff80808080ffff04ff17ff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff20ff1780ffff01ff02ffff01ff02ff10ffff04ff02ffff04ffff02ff2effff04ff02ffff04ff2fff80808080ffff04ff0bff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff02ff05ffff04ffff05ff0b80ffff04ffff05ff1780ffff0180808080ffff01ff02ffff01ff02ff2cffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ffff04ffff04ffff05ff0b80ff2f80ff80808080808080ff0180ffff01ff02ffff01ff02ff2cffff04ff02ffff04ff05ffff04ff0bffff04ffff06ff1780ffff04ffff04ffff05ff1780ff2f80ff80808080808080ff018080ff0180ff018080ff0180ff018080ff0180ff02ff2cffff04ff02ffff04ff05ffff04ff0bffff04ff17ffff01ff80808080808080ffffff02ffff03ff0bffff01ff02ffff01ff02ffff03ffff06ff0b80ffff01ff02ffff01ff02ff2affff04ff02ffff04ffff06ff0180ffff04ffff02ff14ffff04ff02ffff04ff0bff80808080ff8080808080ff0180ffff01ff02ffff010bff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff02ff3affff04ff02ffff04ff03ffff04ffff02ff12ffff04ff02ffff04ff09ffff04ff13ff8080808080ffff04ffff02ff12ffff04ff02ffff04ff09ffff04ff2bff8080808080ff808080808080ff02ff3cffff04ff02ffff04ff11ffff04ff0bffff04ff17ff808080808080ffff02ffff03ff0bffff01ff02ffff01ff02ff16ffff04ff02ffff04ffff04ffff05ff0b80ff0580ffff04ffff06ff0b80ff8080808080ff0180ffff01ff02ffff0105ff018080ff0180ffff02ff16ffff04ff02ffff04ff80ffff04ff05ff8080808080ff15ff17ff0b80ff018080 diff --git a/resources/tests/game-referee-in-cl21/smoke_test_sort.sym b/resources/tests/game-referee-in-cl21/smoke_test_sort.sym new file mode 100644 index 000000000..2a0f8ed38 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/smoke_test_sort.sym @@ -0,0 +1 @@ +{"aa2a43f5d1d2ed44bd135ee807b27cb33eea736d2e50b2787c5fd4100f128f47": "letbinding_$_108", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/smoke_test_sort.clsp", "842642e018168c91bd70ba0be54c311ce3947fe0ce5e418f6a1c88289e11c84b_left_env": "1", "fa9304ef33f250463c38ac3368ad79c5416173e5a666c6571e9b4c5d8b983da1_left_env": "1", "6336806e19a49e9e91a62932f741d94eeca9a3656f35ecf39bc22e15d9940d53_arguments": "(mylist)", "f51796fc523809b04629ca42cffbb3514471eade8cd7d6908f3015f535a97de4": "letbinding_$_107", "842642e018168c91bd70ba0be54c311ce3947fe0ce5e418f6a1c88289e11c84b": "lambda_$_106", "842642e018168c91bd70ba0be54c311ce3947fe0ce5e418f6a1c88289e11c84b_arguments": "(() a b)", "a3ddf8cdd53afc8bcdd3082f709b5f66156f31f8a56e603da27b4e382a68b9f3": "sort", "aa2a43f5d1d2ed44bd135ee807b27cb33eea736d2e50b2787c5fd4100f128f47_left_env": "1", "b8edc250e5666a96397652bb34bdc9979fcd8df7f5aec90e481b7ae4234525aa": "merge", "9d8df0b4587c62404485833b9a811fe717b4f9eb3ff4a11a7806e7fd07a1c5f8": "prepend", "9d8df0b4587c62404485833b9a811fe717b4f9eb3ff4a11a7806e7fd07a1c5f8_arguments": "(a b)", "95e75b36710d3038b1cfe847cc61158c78d91e4d6db6757217c8b32ac4ae13b3_arguments": "(@ everything (rest aggl aggr))", "a3ddf8cdd53afc8bcdd3082f709b5f66156f31f8a56e603da27b4e382a68b9f3_arguments": "(myless mylist)", "9d8df0b4587c62404485833b9a811fe717b4f9eb3ff4a11a7806e7fd07a1c5f8_left_env": "1", "1b7d3888612a5d795887e5e192e68b15536e0e4c08e19324569a576ff06c6299_left_env": "1", "a3ddf8cdd53afc8bcdd3082f709b5f66156f31f8a56e603da27b4e382a68b9f3_left_env": "1", "107eecc0e2d1b059c660f3c81d6e61dd2fc7f71d4f446067132a09d395202140_left_env": "1", "95e75b36710d3038b1cfe847cc61158c78d91e4d6db6757217c8b32ac4ae13b3": "split_inner", "b8edc250e5666a96397652bb34bdc9979fcd8df7f5aec90e481b7ae4234525aa_left_env": "1", "f51796fc523809b04629ca42cffbb3514471eade8cd7d6908f3015f535a97de4_arguments": "((myless_$_91 mylist_$_92) (a b))", "b8edc250e5666a96397652bb34bdc9979fcd8df7f5aec90e481b7ae4234525aa_arguments": "(myless a b)", "107eecc0e2d1b059c660f3c81d6e61dd2fc7f71d4f446067132a09d395202140_arguments": "(vals)", "95e75b36710d3038b1cfe847cc61158c78d91e4d6db6757217c8b32ac4ae13b3_left_env": "1", "f51796fc523809b04629ca42cffbb3514471eade8cd7d6908f3015f535a97de4_left_env": "1", "__chia__main_arguments": "(X)", "107eecc0e2d1b059c660f3c81d6e61dd2fc7f71d4f446067132a09d395202140": "reverse", "6336806e19a49e9e91a62932f741d94eeca9a3656f35ecf39bc22e15d9940d53": "split", "1b7d3888612a5d795887e5e192e68b15536e0e4c08e19324569a576ff06c6299": "reverse_inner", "fa9304ef33f250463c38ac3368ad79c5416173e5a666c6571e9b4c5d8b983da1": "merge_inner", "1b7d3888612a5d795887e5e192e68b15536e0e4c08e19324569a576ff06c6299_arguments": "(reversed rest)", "6336806e19a49e9e91a62932f741d94eeca9a3656f35ecf39bc22e15d9940d53_left_env": "1", "aa2a43f5d1d2ed44bd135ee807b27cb33eea736d2e50b2787c5fd4100f128f47_arguments": "(((myless_$_91 mylist_$_92) (a b)) sa sb)", "fa9304ef33f250463c38ac3368ad79c5416173e5a666c6571e9b4c5d8b983da1_arguments": "(myless A B agg)"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/sort.clinc b/resources/tests/game-referee-in-cl21/sort.clinc new file mode 100644 index 000000000..a9afed46a --- /dev/null +++ b/resources/tests/game-referee-in-cl21/sort.clinc @@ -0,0 +1,44 @@ + +( + (defun split_inner (@ everything (rest aggl aggr)) + (if rest + (split_inner (r rest) aggr (c (f rest) aggl)) + (r everything) + ) + ) + (defun split (mylist) + (split_inner mylist 0 0) + ) + (defun merge_inner (myless A B agg) + ; this should use continued if + (if (not A) + (prepend (reverse agg) B) + (if (not B) + (prepend (reverse agg) A) + (if (a myless (list (f A) (f B))) + (merge_inner myless (r A) B (c (f A) agg)) + (merge_inner myless A (r B) (c (f B) agg)) + ) + ) + ) + ) + (defun merge (myless a b) + (merge_inner myless a b 0) + ) + (defun sort-split (myless (a b)) + (merge myless (sort myless a) (sort myless b)) + ) + (defun sort (myless mylist) + (if mylist + (if (r mylist) + (assign (a b) (split mylist) + sa (sort myless a) + sb (sort myless b) + (merge myless sa sb) + ) + mylist + ) + () + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/spacehandcalc.clinc b/resources/tests/game-referee-in-cl21/spacehandcalc.clinc new file mode 100644 index 000000000..3bfae65ca --- /dev/null +++ b/resources/tests/game-referee-in-cl21/spacehandcalc.clinc @@ -0,0 +1,104 @@ + +; ranks are 1-13 with 1 being two, 12 being king, and 13 being ace +; there are no suits, flushes, or ace-to-four straights +; takes a list of card ranks and returns the value of the best poker +; hand which can be made with them +; returned list is hand type followed by cards in descending order +; all sorting is done highest to lowest +( + (include *standard-cl-22*) + (include sort.clinc) + (include deep_compare.clinc) + (include filtermap.clinc) + (include slice.clinc) + (include max.clinc) + (defconstant FIVE_OF_A_KIND 10) + (defconstant STRAIGHT_FLUSH 9) + (defconstant FOUR_OF_A_KIND 8) + (defconstant FULL_HOUSE 7) + (defconstant FLUSH 6) + (defconstant STRAIGHT 5) + (defconstant THREE_OF_A_KIND 4) + (defconstant TWO_PAIR 3) + (defconstant PAIR 2) + (defconstant HIGH_CARD 1) + (defun straight_high_inner (ranks last count) + (if (not ranks) + ; at the end of the list + 0 + (if (= last (f ranks)) + ; skip identical cards + (straight_high_inner (r ranks) started_ace last count) + ; if the partial straight continues + (if (= (f ranks) (- last 1)) + (if (= count 4) + ; found a straight, add 3 to last because next and last are included + (+ last 3) + ; keep looking for a straight with the count going up by one + (straight_high_inner (r ranks) started_ace (f ranks) (+ count 1)) + ) + ; reset the count + (straight_high_inner (r ranks) started_ace (f ranks) 1) + ) + ) + ) + ) + ; returns the high card of a straight or 0 if there isn't any + ; ranks must be sorted in descending order + (defun straight_high (ranks) + (straight_high_inner (ranks (= (f ranks) 13) 0 0)) + ) + (defun group_by_count_inner (items last count) + (if (not items) + 0 + (if (= (f items) last) + (group_by_count_inner (r items) last (+ count 1)) + (assign val (group_by_count_inner (r items) (f items) 1) + (if last + (c (c count last) val) + val + ) + ) + ) + ) + ) + (defun group_by_count (items) + (group_by_count_inner items 0 0) + ) + (defun space_hand_calc (cards) + (assign + rest (lambda (x) (r x)) + greater (lambda (x y) (> x y)) + ranks (sort greater cards) + sh (straight_high ranks) + max_straight (if sh + (list STRAIGHT sh) + 0 + ) + groups (sort deep> (group_by_count ranks)) + (top_count . top_card) (f groups) + (second_count . second_card) (f (r groups)) + topcards (map rest groups) + max_group (if (= top_count 1) + (c HIGH_CARD (slice topcards 5)) + (if (= top_count 2) + (if (= second_count 1) + (c PAIR (slice topcards 4)) + (c TWO_PAIR (slice topcards 3)) + ) + (if (= top_count 3) + (if (= second_count 1) + (c THREE_OF_A_KIND (slice topcards 3)) + (c FULL_HOUSE (slice topcards 2)) + ) + (if (= top_count 4) + (c FOUR_OF_A_KIND (slice topcards 2)) + (c FIVE_OF_A_KIND (slice topcards 1)) + ) + ) + ) + ) + (max deep< (list max_straight max_group)) + ) + ) +) diff --git a/resources/tests/game-referee-in-cl21/test_handcalc.clsp b/resources/tests/game-referee-in-cl21/test_handcalc.clsp new file mode 100644 index 000000000..16c46f664 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_handcalc.clsp @@ -0,0 +1,379 @@ + +(mod () + (include *standard-cl-21*) + (include deep_compare.clinc) + (include assert.clinc) + (include reverse.clinc) + (include prepend.clinc) + (include map.clinc) + (include filtermap.clinc) + (include slice.clinc) + (include print.clinc) + (include sort.clinc) + (include max.clinc) + (include handcalc.clinc) + + (defun runtests_inner ((myfunc firstarg secondarg . remaining)) + (assign-lambda + firstval (handcalc firstarg) + secondval (handcalc secondarg) + (assert + (a myfunc (list firstval secondval)) + (deep= firstval (handcalc (reverse firstarg))) + (deep= secondval (handcalc (reverse secondarg))) + (if remaining + (runtests_inner remaining) + 0 + ) + ) + ) + ) + + (defun runtests tests (if tests (runtests_inner tests) ())) + + ;; Join these up when large application bug is fixed. + (runtests + ; all beats both emerge over and measure higher + ; straight flush with higher kicker ties + ; A1 K1 Q1 J1 T1 91 = A1 K1 Q1 J1 T1 81 + deep= + (list (c 12 1) (c 11 1) (c 14 1) (c 13 1) (c 10 1) (c 9 1)) + (list (c 12 1) (c 11 1) (c 14 1) (c 13 1) (c 10 1) (c 8 1)) + ; straight flushes of different suits tie + ; A1 K1 Q1 J1 T1 = A2 K2 Q2 J2 T2 + deep= + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 10 1)) + (list (c 14 2) (c 13 2) (c 12 2) (c 11 2) (c 10 2)) + ; higher straight flush beats lower straight flush + ; A1 K1 Q1 J1 T1 > 61 51 41 31 21 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 10 1)) + (list (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + ; A1 K1 Q1 J1 T1 91 = A1 K1 Q1 J1 T1 + deep= + (list (c 12 1) (c 11 1) (c 14 1) (c 13 1) (c 10 1) (c 9 1)) + (list (c 14 2) (c 11 2) (c 10 2) (c 13 2) (c 12 2)) + ; lower (2-6) straight flush beats ace to four straight flush + ; 61 51 41 31 21 > A2 52 42 32 22 + deep> + (list (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 14 2) (c 5 2) (c 4 2) (c 3 2) (c 2 2)) + ; A1 61 51 41 31 21 = 61 51 41 31 21 + deep= + (list (c 14 1) (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + ; ace to four straight flush with higher kicker ties + ; A2 52 42 32 22 61 = A1 51 41 31 21 71 + deep= + (list (c 14 2) (c 5 2) (c 4 2) (c 3 2) (c 2 2) (c 6 1)) + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1) (c 7 1)) + ; ace to four straight flushes of different suits tie + ; A1 51 41 31 21 = A2 52 42 32 22 + deep= + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 14 2) (c 5 2) (c 4 2) (c 3 2) (c 2 2)) + ; ace to four straight flush beats four of a kind + ; A1 51 41 31 21 > K1 K2 K3 K4 J1 + deep> + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 11 1)) + ; A1 A2 A3 A4 51 41 31 21 = A1 51 41 31 21 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + ; four of a kind with higher kicker wins + ; K1 K2 K3 K4 Q1 > K1 K2 K3 K4 J1 + deep> + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 12 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 11 1)) + ; K1 K2 K3 K4 T1 91 = K1 K2 K3 K4 T1 + deep= + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 10 1) (c 9 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 10 1)) + ; four of a kind with higher second kicker ties + ; K1 K2 K3 K4 Q1 J1 = K1 K2 K3 K4 Q1 T1 + deep= + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 12 1) (c 11 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 12 1) (c 10 1)) + ; higher four of a kind beats lower four of a kind + ; K1 K2 K3 K4 21 > 31 32 33 34 A1 + deep> + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 2 1)) + (list (c 3 1) (c 3 2) (c 3 3) (c 3 4) (c 14 1)) + ; K1 K2 K3 K4 31 32 33 34 = K1 K2 K3 K4 32 + deep= + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 3 1) (c 3 2) (c 3 3) (c 3 4)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 3 2)) + ; four of a kind beats full house + ; 21 22 23 24 31 > A1 A2 A3 K1 K2 + deep> + (list (c 2 1) (c 2 2) (c 2 3) (c 2 4) (c 3 1)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2)) + ; 21 22 23 24 A1 A2 A3 = 21 22 23 24 A2 + deep= + (list (c 2 1) (c 2 2) (c 2 3) (c 2 4) (c 14 1) (c 14 2) (c 14 3)) + (list (c 2 1) (c 2 2) (c 2 3) (c 2 4) (c 14 2)) + ; full house with higher set wins + ; 51 52 53 21 22 > 31 32 33 71 72 + deep> + (list (c 5 1) (c 5 2) (c 5 3) (c 2 1) (c 2 2)) + (list (c 3 1) (c 3 2) (c 3 3) (c 7 1) (c 7 2)) + ; A1 A2 A3 K1 K2 K3 = A1 A2 A3 K1 K2 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2) (c 13 3)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2)) + ; full house with same set and higher pair wins + ; 51 52 53 41 42 > 51 52 53 31 32 + deep> + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2)) + (list (c 5 1) (c 5 2) (c 5 3) (c 3 1) (c 3 2)) + ; A1 A2 A3 K1 K2 51 52 = A1 A2 A3 K1 K2 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2) (c 5 1) (c 5 2)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2)) + ; full house ties with two sets + ; 51 52 53 41 42 A1 = 51 52 53 41 42 43 + deep= + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2) (c 14 1)) + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2) (c 4 3)) + ; full house beats flush + ; 51 52 53 41 42 > A1 Q1 T1 81 71 + deep> + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2)) + (list (c 14 1) (c 12 1) (c 10 1) (c 8 1) (c 7 1)) + ; 51 52 53 41 42 A1 K1 Q1 = 51 52 53 41 42 + deep= + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2) (c 14 1) (c 13 1) (c 12 1)) + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2)) + ; higher flush beats lower flush + ; A1 61 51 41 31 > K1 Q1 J1 T1 81 + deep> + (list (c 14 1) (c 6 1) (c 5 1) (c 4 1) (c 3 1)) + (list (c 13 1) (c 12 2) (c 11 1) (c 10 1) (c 8 1)) + ; A1 K1 Q1 J1 81 71 = A1 K1 Q1 J1 81 + deep= + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 8 1) (c 7 1)) + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 8 1)) + ; flush with higher second card wins + ; A1 K1 51 41 31 > A1 Q1 J1 T1 91 + deep> + (list (c 14 1) (c 13 1) (c 5 1) (c 4 1) (c 3 1)) + (list (c 14 1) (c 12 2) (c 11 1) (c 10 1) (c 9 1)) + ; flush with higher third card wins + ; A1 K1 Q1 41 31 > A1 K1 J1 T1 91 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 4 1) (c 3 1)) + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 9 1)) + ; flush with higher fourth card wins + ; A1 K1 Q1 T1 21 > A1 K1 Q1 91 81 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 10 1) (c 2 1)) + (list (c 14 1) (c 13 1) (c 12 1) (c 9 1) (c 8 1)) + ; flush with higher fifth card wins + ; A1 K1 Q1 T1 81 > A1 K1 Q1 T1 71 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 10 1) (c 8 1)) + (list (c 14 1) (c 13 1) (c 12 1) (c 10 1) (c 7 1)) + ; flushes of different suits tie + ; A1 K1 J1 T1 81 = A2 K2 J2 T2 82 + deep= + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 8 1)) + (list (c 14 2) (c 13 2) (c 11 2) (c 10 2) (c 8 2)) + ; same flush with higher sixth card ties + ; A1 K1 J1 T1 81 71 = A1 K1 J1 T1 81 61 + deep= + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 8 1) (c 7 1)) + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 8 1) (c 6 1)) + ; flush beats straight + ; 71 61 51 41 21 > A1 K2 Q3 J4 T1 + deep> + (list (c 7 1) (c 6 1) (c 5 1) (c 4 1) (c 2 1)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1)) + ; A1 K2 Q3 J4 T1 81 71 61 = A1 T1 81 71 61 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 8 1) (c 7 1) (c 6 1)) + (list (c 14 1) (c 10 1) (c 8 1) (c 7 1) (c 6 1)) + ; straight with higher kicker ties + ; A1 K2 Q3 J4 T1 92 = A1 K2 Q3 J4 T1 22 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 9 2)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 2 2)) + ; straights of different suits tie + ; A1 K2 Q3 J4 T1 = A2 K3 Q4 J1 T2 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1)) + (list (c 14 2) (c 13 3) (c 12 4) (c 11 1) (c 10 2)) + ; higher straight beats lower straight + ; A1 K2 Q3 J4 T1 > 61 52 43 34 21 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1)) + (list (c 6 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + ; A1 K2 Q3 J4 T1 92 83 = A1 K2 Q3 J4 T1 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 9 2) (c 8 3)) + (list (c 14 2) (c 13 3) (c 12 4) (c 11 1) (c 10 2)) + ; lower (2-6) straight beats ace to four straight + ; 61 52 43 34 21 > A1 52 43 34 21 + deep> + (list (c 6 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + ; A1 62 53 44 31 22 = 62 53 44 31 22 + deep= + (list (c 14 1) (c 6 2) (c 5 3) (c 4 4) (c 3 1) (c 2 2)) + (list (c 6 2) (c 5 3) (c 4 4) (c 3 1) (c 2 2)) + ; ace to four straight with higher kicker ties + ; A1 52 43 34 21 K2 = A1 52 43 34 21 72 + deep= + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1) (c 13 2)) + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1) (c 7 2)) + ; ace to fours of different suits tie + ; A1 52 43 34 21 = A2 53 44 31 22 + deep= + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 2) (c 5 3) (c 4 4) (c 3 1) (c 2 2)) + ; ace to four straight beats set + ; A1 52 43 34 21 > A1 A2 A3 K1 Q2 + deep> + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 12 2)) + ; A1 A2 A3 52 43 34 21 = A1 52 43 34 21 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 5 2) (c 4 3) (c 3 2) (c 2 1)) + ; higher set wins + ; 71 72 73 34 21 > 51 52 53 A4 K1 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 3 4) (c 2 1)) + (list (c 5 1) (c 5 2) (c 5 3) (c 14 4) (c 13 1)) + ; set with higher first kicker wins + ; 71 72 73 A1 22 > 71 72 73 K1 Q2 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 2 2)) + (list (c 7 1) (c 7 2) (c 7 3) (c 13 1) (c 12 2)) + ; 71 72 73 A1 K2 J3 54 43 = 71 72 73 A1 K2 + deep= + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2) (c 11 3) (c 5 4) (c 4 3)) + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2)) + ; set with higher second kicker wins + ; 71 72 73 A1 K2 > 71 72 73 A1 Q2 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2)) + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 12 2)) + ; set with higher third kicker ties + ; 71 72 73 A1 K2 Q3 = 71 72 73 A1 K2 J3 + deep= + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2) (c 12 3)) + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2) (c 11 3)) + ; set beats two pair + ; 71 72 73 34 21 > A1 A2 K3 K4 Q1 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 14 2) (c 13 3) (c 13 4) (c 12 1)) + ; two pair with higher high pair wins + ; K1 K2 33 34 21 > Q1 Q2 J3 J4 A1 + deep> + (list (c 13 1) (c 13 2) (c 3 3) (c 3 4) (c 2 1)) + (list (c 12 1) (c 12 2) (c 11 3) (c 11 4) (c 14 1)) + ; A1 A2 K1 K2 J1 J2 = A1 A2 K1 K2 J3 + deep= + (list (c 14 1) (c 14 2) (c 13 1) (c 13 2) (c 11 1) (c 11 2)) + (list (c 14 1) (c 14 2) (c 13 1) (c 13 2) (c 11 3)) + ; two pair with tied higher pair and higher lower pair wins + ; K1 K2 71 72 23 > K1 K2 63 64 A1 + deep> + (list (c 13 1) (c 13 2) (c 7 1) (c 7 2) (c 2 3)) + (list (c 13 1) (c 13 2) (c 6 3) (c 6 4) (c 14 1)) + ; two pair with higher kicker wins + ; K1 K2 Q3 Q4 J1 > K1 K2 Q3 Q4 T1 + deep> + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 11 1)) + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 10 1)) + ; K1 K2 Q3 Q4 A1 T1 92 63 = K1 K2 Q3 Q4 A1 + deep= + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 14 1) (c 10 1) (c 9 2) (c 6 3)) + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 14 1)) + ; two pair with higher second kicker ties + ; K1 K2 Q3 Q4 J1 T2 = K1 K2 Q3 Q4 J1 92 + deep= + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 11 1) (c 10 2)) + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 11 1) (c 9 2)) + ; two pair beats pair + ; 41 42 33 34 21 > A1 A2 K3 Q4 J1 + deep> + (list (c 4 1) (c 4 2) (c 3 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 14 2) (c 13 3) (c 12 4) (c 11 1)) + ; higher pair wins + ; 71 72 53 44 31 > 61 62 A3 K4 Q1 + deep> + (list (c 7 1) (c 7 2) (c 5 3) (c 4 4) (c 3 1)) + (list (c 6 1) (c 6 2) (c 14 3) (c 13 4) (c 12 1)) + ; tied pair with higher first kicker wins + ; 91 92 A3 34 21 > 91 92 K3 Q4 J1 + deep> + (list (c 9 1) (c 9 2) (c 14 3) (c 3 4) (c 2 1)) + (list (c 9 1) (c 9 2) (c 13 3) (c 12 4) (c 11 1)) + ; 21 22 A1 Q2 J3 94 81 = 21 22 A1 Q2 J3 + deep= + (list (c 2 1) (c 2 2) (c 14 1) (c 12 2) (c 11 3) (c 9 4) (c 8 1)) + (list (c 2 1) (c 2 2) (c 14 1) (c 12 2) (c 11 3)) + ; tied pair with higher second kicker wins + ; 91 92 A3 K4 21 > 91 92 A3 Q4 J1 + deep> + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 2 1)) + (list (c 9 1) (c 9 2) (c 14 3) (c 12 4) (c 11 1)) + ; tied pair with higher third kicker wins + ; 91 92 A3 K4 Q1 > 91 92 A3 K4 J1 + deep> + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 12 1)) + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 11 1)) + ; tied pair with higher fourth kicker ties + ; 91 92 A3 K4 Q1 J2 = 91 92 A3 K4 Q1 T2 + deep= + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 12 1) (c 11 2)) + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 12 1) (c 10 2)) + ; pair beats high card + ; 21 22 33 44 51 > A1 Q2 J3 T4 91 + deep> + (list (c 2 1) (c 2 2) (c 3 3) (c 4 4) (c 5 1)) + (list (c 14 1) (c 12 2) (c 11 3) (c 10 4) (c 9 1)) + ; higher high card wins + ; A1 22 33 44 61 > K1 Q2 J3 T4 81 + deep> + (list (c 14 1) (c 2 2) (c 3 3) (c 4 4) (c 6 1)) + (list (c 13 1) (c 12 2) (c 11 3) (c 10 4) (c 8 1)) + ; A1 K2 J3 T4 81 72 53 = A1 K2 J3 T4 81 + deep= + (list (c 14 1) (c 13 2) (c 11 3) (c 10 4) (c 8 1) (c 7 2) (c 5 3)) + (list (c 14 1) (c 13 2) (c 11 3) (c 10 4) (c 8 1)) + ; higher second card wins + ; A1 K2 23 34 41 > A1 Q2 J3 T4 91 + deep> + (list (c 14 1) (c 13 2) (c 2 3) (c 3 4) (c 4 1)) + (list (c 14 1) (c 12 2) (c 11 3) (c 10 4) (c 9 1)) + ; higher third card wins + ; A1 K2 Q3 24 41 > A1 K2 J3 T4 91 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 2 4) (c 4 1)) + (list (c 14 1) (c 13 2) (c 11 3) (c 10 4) (c 9 1)) + ; higher fourth card wins + ; A1 K2 Q3 J4 31 > A1 K2 Q3 T4 91 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 3 1)) + (list (c 14 1) (c 13 2) (c 12 3) (c 10 4) (c 9 1)) + ; higher fifth card wins + ; A1 K2 Q3 J4 91 > A1 K2 Q3 J4 81 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 8 1)) + ; higher sixth card ties + ; A1 K2 Q3 J4 91 22 = A1 K2 Q3 J4 91 82 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1) (c 2 2)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1) (c 8 2)) + ; high cards of different suits ties + ; A1 K2 Q3 J4 91 = A2 K3 Q4 J1 92 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1)) + (list (c 14 2) (c 13 3) (c 12 4) (c 11 1) (c 9 2)) + ) +) diff --git a/resources/tests/game-referee-in-cl21/test_handcalc.clvm.hex.reference b/resources/tests/game-referee-in-cl21/test_handcalc.clvm.hex.reference new file mode 100644 index 000000000..441ac847b --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_handcalc.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff8200feffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010180ffff0180808080808080ffff04ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010280ffff04ffff04ffff010affff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010180ffff0180808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff010bffff010280ffff04ffff04ffff010affff010280ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010280ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0102ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff0180808080808080ffff04ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010280ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0102ffff010280ffff04ffff04ffff0106ffff010180ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0107ffff010180ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010280ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0102ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff018080808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff01808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010180ffff0180808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff0180808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff04ffff04ffff010affff010180ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff010effff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0103ffff010480ffff01808080808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff0103ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff0102ffff010380ffff04ffff04ffff0102ffff010480ffff04ffff04ffff0103ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff0102ffff010380ffff04ffff04ffff0102ffff010480ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff018080808080808080ffff04ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff0102ffff010380ffff04ffff04ffff0102ffff010480ffff04ffff04ffff010effff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff01808080808080ffff04ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010dffff010380ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff01808080808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0103ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff018080808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff04ffff04ffff010effff010180ffff0180808080808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff04ffff04ffff0104ffff010380ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0107ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff01808080808080808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff01808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0107ffff010180ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0103ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0107ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010280ffff04ffff04ffff010affff010280ffff04ffff04ffff0108ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0107ffff010180ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010180ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0106ffff010180ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0106ffff010180ffff01808080808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0106ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010280ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff04ffff04ffff0102ffff010280ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff0108ffff010380ffff018080808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0106ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010480ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010280ffff0180808080808080ffff04ffff04ffff04ffff0106ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010480ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff04ffff04ffff010dffff010280ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0107ffff010280ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010480ffff04ffff04ffff0103ffff010180ffff04ffff04ffff0102ffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff018080808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0104ffff010380ffff04ffff04ffff0103ffff010280ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff0105ffff010180ffff04ffff04ffff0105ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff010effff010480ffff04ffff04ffff010dffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff0102ffff010280ffff01808080808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff0105ffff010480ffff04ffff04ffff0104ffff010380ffff01808080808080808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff01808080808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010280ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff0180808080808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010380ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0107ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010cffff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff010effff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010180ffff04ffff04ffff010bffff010280ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010380ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0102ffff010380ffff01808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff0106ffff010380ffff04ffff04ffff0106ffff010480ffff04ffff04ffff010effff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010affff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010effff010180ffff04ffff04ffff010affff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff0106ffff010380ffff01808080808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010effff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff04ffff04ffff010affff010280ffff0180808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff04ffff04ffff0109ffff010280ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0104ffff010180ffff04ffff04ffff0104ffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0107ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0105ffff010380ffff04ffff04ffff0104ffff010480ffff04ffff04ffff0103ffff010180ffff01808080808080ffff04ffff04ffff04ffff0106ffff010180ffff04ffff04ffff0106ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff0109ffff010480ffff04ffff04ffff0108ffff010180ffff018080808080808080ffff04ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010380ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff0102ffff010180ffff01808080808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff01808080808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010bffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff04ffff04ffff010bffff010280ffff0180808080808080ffff04ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0109ffff010280ffff04ffff04ffff010effff010380ffff04ffff04ffff010dffff010480ffff04ffff04ffff010cffff010180ffff04ffff04ffff010affff010280ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff0102ffff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0104ffff010480ffff04ffff04ffff0105ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff0102ffff010280ffff04ffff04ffff0103ffff010380ffff04ffff04ffff0104ffff010480ffff04ffff04ffff0106ffff010180ffff01808080808080ffff04ffff04ffff04ffff010dffff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0108ffff010180ffff04ffff04ffff0107ffff010280ffff04ffff04ffff0105ffff010380ffff018080808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff0102ffff010380ffff04ffff04ffff0103ffff010480ffff04ffff04ffff0104ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010cffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff0102ffff010480ffff04ffff04ffff0104ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010bffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff0103ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010affff010480ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff5080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff0108ffff010180ffff01808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0102ffff010280ffff0180808080808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff0109ffff010180ffff04ffff04ffff0108ffff010280ffff0180808080808080ffff04ffff04ffff0102ffff04ffff04ffff0101ff8200b080ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff80808080ffff04ffff04ffff04ffff010effff010180ffff04ffff04ffff010dffff010280ffff04ffff04ffff010cffff010380ffff04ffff04ffff010bffff010480ffff04ffff04ffff0109ffff010180ffff01808080808080ffff04ffff04ffff04ffff010effff010280ffff04ffff04ffff010dffff010380ffff04ffff04ffff010cffff010480ffff04ffff04ffff010bffff010180ffff04ffff04ffff0109ffff010280ffff01808080808080ff808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080ffff04ffff01ffffffffffff02ffff03ffff07ff0580ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff02ff8200a0ffff04ff02ffff04ffff06ff0180ffff04ffff02ff40ffff04ff02ffff04ffff05ff0580ffff04ffff05ff0b80ff8080808080ff8080808080ff0180ffff01ff02ffff01ff0101ff018080ff0180ff0180ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff0181ffff0180ffff01ff02ffff01ff02ffff03ffff15ff05ff0b80ffff01ff02ffff01ff0101ff0180ffff01ff02ffff01ff11ffff0180ffff15ff0bff058080ff018080ff0180ff018080ff0180ff018080ff0180ffff02ffff03ff0bffff01ff02ffff010bff0180ffff01ff02ffff01ff02ff40ffff04ff02ffff04ffff06ff0980ffff04ffff06ff1580ff8080808080ff018080ff0180ff09ffff02ff40ffff04ff02ffff04ff05ffff04ff0bff8080808080ffff0181ff80ffff09ffff02ff40ffff04ff02ffff04ff05ffff04ff0bff8080808080ffff010180ffff09ffff02ff40ffff04ff02ffff04ff05ffff04ff0bff8080808080ff8080ff02ffff03ff0bffff01ff02ffff01ff02ff8200f0ffff04ff02ffff04ffff04ffff05ff0b80ff0580ffff04ffff06ff0b80ff8080808080ff0180ffff01ff02ffff0105ff018080ff0180ffffff02ff8200f0ffff04ff02ffff04ff80ffff04ff05ff8080808080ffff02ffff03ff05ffff01ff02ffff01ff04ffff05ff0580ffff02ff8200a8ffff04ff02ffff04ffff06ff0580ffff04ff0bff808080808080ff0180ffff01ff02ffff010bff018080ff0180ff02ffff03ff0bffff01ff02ffff01ff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff02ff8200e8ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff80808080808080ff0180ffff01ff02ffff0117ff018080ff0180ffff02ffff03ff0bffff01ff02ffff01ff02ff8200b8ffff04ff02ffff04ffff06ff0180ffff04ffff02ff05ffff04ffff05ff0b80ffff01808080ff8080808080ff0180ffff01ff02ffff0117ff018080ff0180ffff02ffff03ff0bffff01ff02ffff01ff04ff0bffff02ff58ffff04ff02ffff04ff09ffff04ffff06ff1580ffff04ff2dff80808080808080ff0180ffff01ff02ffff01ff02ff58ffff04ff02ffff04ff09ffff04ffff06ff1580ffff04ff2dff808080808080ff018080ff0180ff02ffff03ffff02ff44ffff04ff02ffff04ffff04ffff018c736c69636520696e70757473ffff04ffff01866e796c697374ffff04ff0bff80808080ffff04ffff20ff0b80ff8080808080ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff04ffff05ff0580ffff02ff8200f8ffff04ff02ffff04ffff06ff0580ffff04ffff11ff0bffff010180ff808080808080ff018080ff0180ffffffff02ffff03ffff22ffff0187247072696e7424ff05ff0b80ffff01ff02ffff010bff0180ffff01ff02ffff010bff018080ff0180ffff02ffff03ff05ffff01ff02ffff01ff02ff8200a4ffff04ff02ffff04ffff06ff0580ffff04ff17ffff04ffff04ffff05ff0580ff0b80ff808080808080ff0180ffff01ff02ffff01ff06ff0380ff018080ff0180ff02ff8200a4ffff04ff02ffff04ff05ffff01ff80ff8080808080ffff02ffff03ffff20ff0b80ffff01ff02ffff01ff02ff8200a8ffff04ff02ffff04ffff02ff48ffff04ff02ffff04ff2fff80808080ffff04ff17ff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff20ff1780ffff01ff02ffff01ff02ff8200a8ffff04ff02ffff04ffff02ff48ffff04ff02ffff04ff2fff80808080ffff04ff0bff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff02ff05ffff04ffff05ff0b80ffff04ffff05ff1780ffff0180808080ffff01ff02ffff01ff02ff54ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ffff04ffff04ffff05ff0b80ff2f80ff80808080808080ff0180ffff01ff02ffff01ff02ff54ffff04ff02ffff04ff05ffff04ff0bffff04ffff06ff1780ffff04ffff04ffff05ff1780ff2f80ff80808080808080ff018080ff0180ff018080ff0180ff018080ff0180ffff02ff54ffff04ff02ffff04ff05ffff04ff0bffff04ff17ffff01ff80808080808080ff02ffff03ff0bffff01ff02ffff01ff02ffff03ffff06ff0b80ffff01ff02ffff01ff02ff4cffff04ff02ffff04ffff06ff0180ffff04ffff02ff8200e4ffff04ff02ffff04ff0bff80808080ff8080808080ff0180ffff01ff02ffff010bff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffffff02ff8200acffff04ff02ffff04ff03ffff04ffff02ff8200f4ffff04ff02ffff04ff09ffff04ff13ff8080808080ffff04ffff02ff8200f4ffff04ff02ffff04ff09ffff04ff2bff8080808080ff808080808080ffff02ff8200b4ffff04ff02ffff04ff11ffff04ff0bffff04ff17ff808080808080ff02ffff03ffff20ff1780ffff01ff02ffff010bff0180ffff01ff02ffff01ff02ffff03ffff02ff05ffff04ff0bffff04ffff05ff1780ffff0180808080ffff01ff02ffff01ff02ff8200ecffff04ff02ffff04ff05ffff04ffff05ff1780ffff04ffff06ff1780ff808080808080ff0180ffff01ff02ffff01ff02ff8200ecffff04ff02ffff04ff05ffff04ff0bffff04ffff06ff1780ff808080808080ff018080ff0180ff018080ff0180ffff02ff8200ecffff04ff02ffff04ff05ffff04ff13ffff04ff1bff808080808080ffff02ffff03ffff20ff0580ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff02ffff03ffff09ffff05ff0580ff0b80ffff01ff02ffff01ff02ffff03ffff09ff17ffff010480ffff01ff02ffff010bff0180ffff01ff02ffff01ff02ff8200bcffff04ff02ffff04ffff06ff0580ffff04ff0bffff04ffff10ff17ffff010180ff808080808080ff018080ff0180ff0180ffff01ff02ffff01ff02ff8200bcffff04ff02ffff04ffff06ff0580ffff04ffff05ff0580ffff04ffff0101ff808080808080ff018080ff0180ff018080ff0180ff02ff8200bcffff04ff02ffff04ffff02ff8200f4ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff4280ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ffff04ff05ff8080808080ffff01ff80ff8080808080ffffffffff02ff50ffff04ff02ffff04ff0bffff04ff17ff8080808080ffff02ffff03ffff20ff0580ffff01ff02ffff01ff02ffff03ffff18ffff09ff17ffff010280ffff09ff2fffff010480ff0b80ffff01ff02ffff01ff0105ff0180ffff01ff02ffff01ff0180ff018080ff0180ff0180ffff01ff02ffff01ff02ffff03ffff09ff17ffff05ff058080ffff01ff02ffff01ff02ff8200a2ffff04ff02ffff04ffff06ff0580ffff04ff0bffff04ff17ffff04ff2fff80808080808080ff0180ffff01ff02ffff01ff02ffff03ffff09ffff05ff0580ffff11ff17ffff01018080ffff01ff02ffff01ff02ffff03ffff09ff2fffff010480ffff01ff02ffff01ff10ff17ffff010380ff0180ffff01ff02ffff01ff02ff8200a2ffff04ff02ffff04ffff06ff0580ffff04ff0bffff04ffff05ff0580ffff04ffff10ff2fffff010180ff80808080808080ff018080ff0180ff0180ffff01ff02ffff01ff02ff8200a2ffff04ff02ffff04ffff06ff0580ffff04ff0bffff04ffff05ff0580ffff04ffff0101ff80808080808080ff018080ff0180ff018080ff0180ff018080ff0180ff02ff8200a2ffff04ff02ffff04ff05ffff04ffff09ff09ffff010e80ffff01ff80ff808080808080ffff02ffff03ffff20ff0580ffff01ff02ffff01ff04ffff04ff17ff0b80ffff018080ff0180ffff01ff02ffff01ff02ffff03ffff09ffff05ff0580ff0b80ffff01ff02ffff01ff02ff52ffff04ff02ffff04ffff06ff0580ffff04ff0bffff04ffff10ff17ffff010180ff808080808080ff0180ffff01ff02ffff01ff02ff8200b2ffff04ff02ffff04ffff06ff0180ffff04ffff02ff52ffff04ff02ffff04ffff06ff0580ffff04ffff05ff0580ffff04ffff0101ff808080808080ff8080808080ff018080ff0180ff018080ff0180ffff04ffff04ff2dff1580ff0b80ff02ff52ffff04ff02ffff04ff05ffff04ff09ffff01ff808080808080ffffff02ff5affff04ff02ffff04ff03ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200aa80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200ea80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ff808080808080ff131bffff02ff8200faffff04ff02ffff04ff03ffff04ffff02ff8200fcffff04ff02ffff04ffff02ff8200e8ffff04ff02ffff04ff17ffff04ff09ffff01ff808080808080ff80808080ffff04ffff02ff8200f4ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200ba80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ffff04ffff02ff8200e8ffff04ff02ffff04ff0bffff04ff09ffff01ff808080808080ff8080808080ff808080808080ffff02ff50ffff04ff02ffff04ff0bffff04ff17ff8080808080ff02ff8200f6ffff04ff02ffff04ff03ffff04ffff02ffff03ffff20ff0b80ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff02ff8200e6ffff04ff02ffff04ffff06ff0180ffff04ffff02ff8200f4ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff4680ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ffff02ff58ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200a680ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff04ff0bffff01808080ffff04ffff0101ffff0180808080ffff0180808080ffff04ff11ffff04ffff0180ff808080808080ff8080808080ff8080808080ff018080ff0180ffff04ffff02ff8200e2ffff04ff02ffff04ff17ff80808080ffff04ffff02ff8200f4ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200b680ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ffff04ffff02ff8200f2ffff04ff02ffff04ff17ff80808080ff8080808080ff80808080808080ffffffff02ff50ffff04ff02ffff04ff0bffff04ff17ff8080808080ffff02ffff03ffff09ff09ff1b80ffff01ff02ffff0113ff0180ffff01ff02ffff01ff0180ff018080ff0180ff02ff56ffff04ff02ffff04ff03ffff04ffff02ff8200e2ffff04ff02ffff04ff0bff80808080ff8080808080ffff02ffff03ff0bffff01ff02ffff01ff04ffff0109ffff04ff0bffff01808080ff0180ffff01ff02ffff01ff04ffff0106ffff02ff8200f8ffff04ff02ffff04ff15ffff04ffff0105ff808080808080ff018080ff0180ffff02ff50ffff04ff02ffff04ff0bffff04ff17ff8080808080ff02ff4effff04ff02ffff04ff03ffff04ffff02ffff03ff17ffff01ff02ffff01ff04ffff0105ffff04ff17ffff01808080ff0180ffff01ff02ffff01ff0180ff018080ff0180ffff04ff4fffff04ff81afffff04ffff02ff8200e8ffff04ff02ffff04ff59ffff04ff2fffff01ff808080808080ff8080808080808080ffffff02ff8200aeffff04ff02ffff04ff03ffff04ffff02ffff03ffff09ff27ffff010180ffff01ff02ffff01ff04ffff0101ffff02ff8200f8ffff04ff02ffff04ff5fffff04ffff0105ff808080808080ff0180ffff01ff02ffff01ff02ffff03ffff09ff27ffff010280ffff01ff02ffff01ff02ffff03ffff09ff4fffff010180ffff01ff02ffff01ff04ffff0102ffff02ff8200f8ffff04ff02ffff04ff5fffff04ffff0104ff808080808080ff0180ffff01ff02ffff01ff04ffff0103ffff02ff8200f8ffff04ff02ffff04ff5fffff04ffff0103ff808080808080ff018080ff0180ff0180ffff01ff02ffff01ff02ffff03ffff09ff27ffff010380ffff01ff02ffff01ff02ffff03ffff09ff4fffff010180ffff01ff02ffff01ff04ffff0104ffff02ff8200f8ffff04ff02ffff04ff5fffff04ffff0103ff808080808080ff0180ffff01ff02ffff01ff04ffff0107ffff02ff8200f8ffff04ff02ffff04ff5fffff04ffff0102ff808080808080ff018080ff0180ff0180ffff01ff02ffff01ff04ffff0108ffff02ff8200f8ffff04ff02ffff04ff5fffff04ffff0102ff808080808080ff018080ff0180ff018080ff0180ff018080ff0180ff8080808080ffff02ff5cffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200ee80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ffff04ffff04ff29ffff04ff15ffff04ff0bff80808080ff8080808080ff02ff8200e0ffff04ff02ffff04ff0bffff04ff17ff8080808080ffff02ff8200beffff04ff02ffff04ff03ffff04ffff02ff4affff04ff02ffff04ff15ff80808080ffff04ffff02ff4affff04ff02ffff04ff2dff80808080ff808080808080ffff02ffff03ffff02ff11ffff04ff0bffff04ff17ff80808080ffff01ff02ffff01ff02ffff03ffff02ff8200b0ffff04ff02ffff04ff0bffff04ffff02ff4affff04ff02ffff04ffff02ff48ffff04ff02ffff04ff29ff80808080ff80808080ff8080808080ffff01ff02ffff01ff02ffff03ffff02ff8200b0ffff04ff02ffff04ff17ffff04ffff02ff4affff04ff02ffff04ffff02ff48ffff04ff02ffff04ff59ff80808080ff80808080ff8080808080ffff01ff02ffff01ff02ffff03ff79ffff01ff02ffff01ff02ff5effff04ff02ffff04ff79ff80808080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ff02ffff03ff03ffff01ff02ffff01ff02ff5effff04ff02ffff04ff03ff80808080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff018080 diff --git a/resources/tests/game-referee-in-cl21/test_handcalc.sym b/resources/tests/game-referee-in-cl21/test_handcalc.sym new file mode 100644 index 000000000..4ff5ca1eb --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_handcalc.sym @@ -0,0 +1 @@ +{"dd3953423ae82b313d5361cb4c9ea7d18b43294e1c0801e1c021d101a7ce58f6_left_env": "1", "24d7dbd0fece56391d516812ca037a910985789b424144b4dc8f8c57cf3ce108": "straight_high_inner", "e51e3ecc739e0c9b646889ad797fa268d9acbb09f42f70b1a75be411440dcf36_left_env": "1", "76751a9c95444501c3ad57278f9ace3e6a1e41fca43bd93d6e5a8ca125e8c4a3_arguments": "(((myless_$_453 mylist_$_454) (a b)) sa sb)", "3ac370c3d91282c6eb94742d40cdb371de392eaca8a0a213d3d3ade6d1b03367_left_env": "1", "36900466483b112787abffe419202a499c8e5467e750cf3aec03ac5fd0ba9cfd_left_env": "1", "36900466483b112787abffe419202a499c8e5467e750cf3aec03ac5fd0ba9cfd_arguments": "(myless mylist)", "40fe864fddcab141770d7e0c0be5d5d07bcb8efd496a6f4bb2cdc3834e94eab9_left_env": "1", "c4e96a2219a3d6389693cfb81c4309f5bf5f1a89f300c7c12d019e08d14cde09_arguments": "(a b)", "1c2f95563ff25508afc464b19cd91787c95ac0b12f1d4fa16addeaa6335aac8d_arguments": "((((((cards_$_473) first rest) fsuit nosuits) max_flush sh groups) max_straight (top_count . top_card) (second_count . second_card) topcards) max_group)", "01ed928ebc640669bfdc000b292962b919f6635176dd9119325f5f912d7567b8_arguments": "(() x y)", "a87d2e2e13539c8f9f66137bdee9a7fd750b8bdb6a07fc1e6cd8416d771d9958_arguments": "(() x y)", "c60f4e2e12ab7285ef437f63bb24f4e5e6267fc76479893a179ebae738aca004": "letbinding_$_481", "64499f3fb4ed1322a24ece83ce5664a10e41b8cb291c4c395d961004680802ca_arguments": "(process remaining init)", "ff5f6181808cb059cffecd54a408a6b4566b861a8905208bbeb87a10e3c8ec65": "merge", "d8c50d6282a1ba47f0a23430d177bbfbb72e2b84713745e894f575570f1f3d6e": "lambda_$_486", "973ed02dde4588cd7699bdec8c1138e788edf49380ca7a8c62c74ad021bf8022_arguments": "((((cards_$_473) first rest) fsuit nosuits) fnosuits)", "1bde90fbef9d7a4c5cb7133780d4275aa03dfc0a54a905b899d65b4b578f6661": "letbinding_$_479", "4b216afca35845442571c30c6d827724285ca82490dbdae1b27f2d082e85ff66": "runtests", "64499f3fb4ed1322a24ece83ce5664a10e41b8cb291c4c395d961004680802ca_left_env": "1", "f5470fda7b930fc12a27be2810e4c48ad7e35ceda81273ba026cd6da98d94c3c_left_env": "1", "5c2c8bfc9ac6c103562c9017a5cee2634da7ee43d44bd4f3d46d11e519942ea6": "straight_high", "724d50ee95e66b90e255e2cecc58cc35c2eb27302b1952948538ad95f7387a4b_arguments": "(F L R)", "24d7dbd0fece56391d516812ca037a910985789b424144b4dc8f8c57cf3ce108_arguments": "(ranks started_ace last count)", "ea58983a62a5d0a526c32f3423717af26c6d9d5963eb4831a0d1049227eba060_arguments": "(suits last count)", "26df51fddc60f80f199438e505e3832df819036418c8bf439a572679ebfd1897": "split_inner", "d8c50d6282a1ba47f0a23430d177bbfbb72e2b84713745e894f575570f1f3d6e_left_env": "1", "2379a74341919286585b51fa8ff2bf5a2e76254197c786fdcd96d4b004be17ba": "deep>", "3ac370c3d91282c6eb94742d40cdb371de392eaca8a0a213d3d3ade6d1b03367_arguments": "(a b)", "7245d12b7785f1ee23a0ec0d3a03432057ec41fb0561d373d91cda267f44c3e9": "reverse", "b131c2e07b1152ba1bd67a5398a6b58cde3020e01fa510f0f7c3cd502fcdb30d_left_env": "1", "c64dc59aae25053fe8d872e13fca4e2c43765a1a83335d520435180f3ab55485_arguments": "(myless A B agg)", "d121f71fcb5513b6d2b097255deb884ff40c26dd366b0ba7cf2eea1440b07901_left_env": "1", "6776e296095747790c1a208caea4e4b8633e2c146890367f13a0238f42b28c87_left_env": "1", "0dda9e9f845e653fbb9f1065f098a0c439bf605b2e8830546184c95eae822f25_arguments": "((process_$_431 remaining_$_432 init_$_433) next)", "76751a9c95444501c3ad57278f9ace3e6a1e41fca43bd93d6e5a8ca125e8c4a3_left_env": "1", "20f4eca5a09be558c75518290c48d6aad0e4e8306e79cf03e92b8ca828d35f99": "runtests_inner", "c4f6f8551c01bbcf52ab1fd2b92333649110b28aee8570a9422e2b3a671fe94b_arguments": "((((cards_$_473) first rest) fsuit nosuits) max_flush sh groups)", "d121f71fcb5513b6d2b097255deb884ff40c26dd366b0ba7cf2eea1440b07901_arguments": "(mylist)", "00c86eab1caeadaaf967420becbefef7170e5e996890edc355d16c263ea32e27_arguments": "(myless best_so_far mylist)", "ca3aa00f92cd656fbf2e7c7863a2bd3ebfcdf0bdb05ca8f00ce9d51648d38b1f_arguments": "(a b)", "042c55b626937b8d6be258a97f8d25cdedeee2e712dbddef9438fa900eff883e_left_env": "1", "24d7dbd0fece56391d516812ca037a910985789b424144b4dc8f8c57cf3ce108_left_env": "1", "6776e296095747790c1a208caea4e4b8633e2c146890367f13a0238f42b28c87_arguments": "(((((cards_$_473) first rest) fsuit nosuits) max_flush sh groups) max_straight (top_count . top_card) (second_count . second_card) topcards)", "40fe864fddcab141770d7e0c0be5d5d07bcb8efd496a6f4bb2cdc3834e94eab9_arguments": "((items_$_469 last_$_470 count_$_471) val)", "0dda9e9f845e653fbb9f1065f098a0c439bf605b2e8830546184c95eae822f25_left_env": "1", "5c2c8bfc9ac6c103562c9017a5cee2634da7ee43d44bd4f3d46d11e519942ea6_arguments": "(ranks)", "8bda8ccb43702a3e1a1b2354adfb39e9a229ae4dc896e7481a569ecc19dd2d6d": "deep<", "0dda9e9f845e653fbb9f1065f098a0c439bf605b2e8830546184c95eae822f25": "letbinding_$_480", "724d50ee95e66b90e255e2cecc58cc35c2eb27302b1952948538ad95f7387a4b": "map-with-rest", "40fe864fddcab141770d7e0c0be5d5d07bcb8efd496a6f4bb2cdc3834e94eab9": "letbinding_$_484", "8c4576eb20d2f8cc795c065e3a585703145c1f6beff372bf37b4634ad785adc8": "letbinding_$_488", "ff5f6181808cb059cffecd54a408a6b4566b861a8905208bbeb87a10e3c8ec65_left_env": "1", "ea58983a62a5d0a526c32f3423717af26c6d9d5963eb4831a0d1049227eba060_left_env": "1", "2379a74341919286585b51fa8ff2bf5a2e76254197c786fdcd96d4b004be17ba_arguments": "(a b)", "8c4576eb20d2f8cc795c065e3a585703145c1f6beff372bf37b4634ad785adc8_left_env": "1", "__chia__main_arguments": "()", "b131c2e07b1152ba1bd67a5398a6b58cde3020e01fa510f0f7c3cd502fcdb30d": "letbinding_$_485", "c64b0acdb9bfc5a1bec80732d64bee90fd12e869207674a220042725441f9cb3": "find_flush", "7245d12b7785f1ee23a0ec0d3a03432057ec41fb0561d373d91cda267f44c3e9_arguments": "(vals)", "a87d2e2e13539c8f9f66137bdee9a7fd750b8bdb6a07fc1e6cd8416d771d9958": "lambda_$_494", "01ed928ebc640669bfdc000b292962b919f6635176dd9119325f5f912d7567b8": "lambda_$_498", "b16167d2466d1fb9c6fd4b5636fa809f866e9b7a0a698f1a18ffa308d7280c42": "letbinding_$_499", "f5470fda7b930fc12a27be2810e4c48ad7e35ceda81273ba026cd6da98d94c3c": "slice", "76751a9c95444501c3ad57278f9ace3e6a1e41fca43bd93d6e5a8ca125e8c4a3": "letbinding_$_482", "1c2f95563ff25508afc464b19cd91787c95ac0b12f1d4fa16addeaa6335aac8d": "letbinding_$_497", "042c55b626937b8d6be258a97f8d25cdedeee2e712dbddef9438fa900eff883e_arguments": "(R P)", "8bda8ccb43702a3e1a1b2354adfb39e9a229ae4dc896e7481a569ecc19dd2d6d_left_env": "1", "26df51fddc60f80f199438e505e3832df819036418c8bf439a572679ebfd1897_arguments": "(@ everything (rest aggl aggr))", "24d45638962c8ac6b0923f0f2c912177b1b1c710bbcd3e10fccf01fc6f771263_arguments": "(myless mylist)", "d013fcaaa576d263aa86406509a11272424931651d958cff6ba9be6d2b9a7fed_arguments": "(items last count)", "24d45638962c8ac6b0923f0f2c912177b1b1c710bbcd3e10fccf01fc6f771263": "sort", "4b216afca35845442571c30c6d827724285ca82490dbdae1b27f2d082e85ff66_arguments": "tests", "b131c2e07b1152ba1bd67a5398a6b58cde3020e01fa510f0f7c3cd502fcdb30d_arguments": "((cards_$_473) first rest)", "c64dc59aae25053fe8d872e13fca4e2c43765a1a83335d520435180f3ab55485": "merge_inner", "973ed02dde4588cd7699bdec8c1138e788edf49380ca7a8c62c74ad021bf8022": "letbinding_$_491", "1bde90fbef9d7a4c5cb7133780d4275aa03dfc0a54a905b899d65b4b578f6661_left_env": "1", "973ed02dde4588cd7699bdec8c1138e788edf49380ca7a8c62c74ad021bf8022_left_env": "1", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/test_handcalc.clsp", "ca3aa00f92cd656fbf2e7c7863a2bd3ebfcdf0bdb05ca8f00ce9d51648d38b1f_left_env": "1", "20f4eca5a09be558c75518290c48d6aad0e4e8306e79cf03e92b8ca828d35f99_arguments": "((myfunc firstarg secondarg . remaining))", "471488bbd4b64848f0c6592900ef2a751e8907a72557b0ba794e0d6f1252c374_left_env": "1", "ff5f6181808cb059cffecd54a408a6b4566b861a8905208bbeb87a10e3c8ec65_arguments": "(myless a b)", "00c86eab1caeadaaf967420becbefef7170e5e996890edc355d16c263ea32e27": "max_inner", "26df51fddc60f80f199438e505e3832df819036418c8bf439a572679ebfd1897_left_env": "1", "00c86eab1caeadaaf967420becbefef7170e5e996890edc355d16c263ea32e27_left_env": "1", "d013fcaaa576d263aa86406509a11272424931651d958cff6ba9be6d2b9a7fed": "group_by_count_inner", "c4e96a2219a3d6389693cfb81c4309f5bf5f1a89f300c7c12d019e08d14cde09": "prepend", "145bfb83f7b3ef33ac1eada788c187e4d1feb7326bcf340bb060a62e75434854_left_env": "1", "1c2f95563ff25508afc464b19cd91787c95ac0b12f1d4fa16addeaa6335aac8d_left_env": "1", "f4c3c71dddb1c7ee50bc104032f26172d61ff77f9bca9876d1846c47cd72020e": "handcalc", "7245d12b7785f1ee23a0ec0d3a03432057ec41fb0561d373d91cda267f44c3e9_left_env": "1", "a87d2e2e13539c8f9f66137bdee9a7fd750b8bdb6a07fc1e6cd8416d771d9958_left_env": "1", "c64b0acdb9bfc5a1bec80732d64bee90fd12e869207674a220042725441f9cb3_arguments": "(suits)", "c4e96a2219a3d6389693cfb81c4309f5bf5f1a89f300c7c12d019e08d14cde09_left_env": "1", "36900466483b112787abffe419202a499c8e5467e750cf3aec03ac5fd0ba9cfd": "max", "d013fcaaa576d263aa86406509a11272424931651d958cff6ba9be6d2b9a7fed_left_env": "1", "1bde90fbef9d7a4c5cb7133780d4275aa03dfc0a54a905b899d65b4b578f6661_arguments": "((a_$_404 b_$_405) inner_result)", "64499f3fb4ed1322a24ece83ce5664a10e41b8cb291c4c395d961004680802ca": "filtermap", "e51e3ecc739e0c9b646889ad797fa268d9acbb09f42f70b1a75be411440dcf36": "lambda_$_493", "c4f6f8551c01bbcf52ab1fd2b92333649110b28aee8570a9422e2b3a671fe94b_left_env": "1", "79f3cf5c572162105f8688174041050314a154956ef27d7ea6414a9dcd402faa_arguments": "(items)", "042c55b626937b8d6be258a97f8d25cdedeee2e712dbddef9438fa900eff883e": "print", "e51e3ecc739e0c9b646889ad797fa268d9acbb09f42f70b1a75be411440dcf36_arguments": "((fsuit) (card_rank . card_suit))", "f4c3c71dddb1c7ee50bc104032f26172d61ff77f9bca9876d1846c47cd72020e_arguments": "(cards)", "b16167d2466d1fb9c6fd4b5636fa809f866e9b7a0a698f1a18ffa308d7280c42_arguments": "(((myfunc_$_474 firstarg_$_475 secondarg_$_476 . remaining_$_477)) firstval secondval)", "8bda8ccb43702a3e1a1b2354adfb39e9a229ae4dc896e7481a569ecc19dd2d6d_arguments": "(a b)", "471488bbd4b64848f0c6592900ef2a751e8907a72557b0ba794e0d6f1252c374": "reverse_inner", "f5470fda7b930fc12a27be2810e4c48ad7e35ceda81273ba026cd6da98d94c3c_arguments": "(mylist count)", "c64dc59aae25053fe8d872e13fca4e2c43765a1a83335d520435180f3ab55485_left_env": "1", "79f3cf5c572162105f8688174041050314a154956ef27d7ea6414a9dcd402faa": "group_by_count", "b16167d2466d1fb9c6fd4b5636fa809f866e9b7a0a698f1a18ffa308d7280c42_left_env": "1", "2379a74341919286585b51fa8ff2bf5a2e76254197c786fdcd96d4b004be17ba_left_env": "1", "724d50ee95e66b90e255e2cecc58cc35c2eb27302b1952948538ad95f7387a4b_left_env": "1", "5c2c8bfc9ac6c103562c9017a5cee2634da7ee43d44bd4f3d46d11e519942ea6_left_env": "1", "c64b0acdb9bfc5a1bec80732d64bee90fd12e869207674a220042725441f9cb3_left_env": "1", "79f3cf5c572162105f8688174041050314a154956ef27d7ea6414a9dcd402faa_left_env": "1", "145bfb83f7b3ef33ac1eada788c187e4d1feb7326bcf340bb060a62e75434854": "lambda_$_487", "c60f4e2e12ab7285ef437f63bb24f4e5e6267fc76479893a179ebae738aca004_arguments": "((myless_$_453 mylist_$_454) (a b))", "dd3953423ae82b313d5361cb4c9ea7d18b43294e1c0801e1c021d101a7ce58f6_arguments": "(((((cards_$_473) first rest) fsuit nosuits) fnosuits) fsh)", "d8c50d6282a1ba47f0a23430d177bbfbb72e2b84713745e894f575570f1f3d6e_arguments": "(() x)", "f4c3c71dddb1c7ee50bc104032f26172d61ff77f9bca9876d1846c47cd72020e_left_env": "1", "6776e296095747790c1a208caea4e4b8633e2c146890367f13a0238f42b28c87": "letbinding_$_496", "24d45638962c8ac6b0923f0f2c912177b1b1c710bbcd3e10fccf01fc6f771263_left_env": "1", "d121f71fcb5513b6d2b097255deb884ff40c26dd366b0ba7cf2eea1440b07901": "split", "01ed928ebc640669bfdc000b292962b919f6635176dd9119325f5f912d7567b8_left_env": "1", "4b216afca35845442571c30c6d827724285ca82490dbdae1b27f2d082e85ff66_left_env": "1", "c4f6f8551c01bbcf52ab1fd2b92333649110b28aee8570a9422e2b3a671fe94b": "letbinding_$_490", "dd3953423ae82b313d5361cb4c9ea7d18b43294e1c0801e1c021d101a7ce58f6": "letbinding_$_495", "c60f4e2e12ab7285ef437f63bb24f4e5e6267fc76479893a179ebae738aca004_left_env": "1", "ca3aa00f92cd656fbf2e7c7863a2bd3ebfcdf0bdb05ca8f00ce9d51648d38b1f": "deep=", "ea58983a62a5d0a526c32f3423717af26c6d9d5963eb4831a0d1049227eba060": "find_flush_inner", "20f4eca5a09be558c75518290c48d6aad0e4e8306e79cf03e92b8ca828d35f99_left_env": "1", "8c4576eb20d2f8cc795c065e3a585703145c1f6beff372bf37b4634ad785adc8_arguments": "(((cards_$_473) first rest) fsuit nosuits)", "145bfb83f7b3ef33ac1eada788c187e4d1feb7326bcf340bb060a62e75434854_arguments": "(() x)", "3ac370c3d91282c6eb94742d40cdb371de392eaca8a0a213d3d3ade6d1b03367": "deep_compare", "471488bbd4b64848f0c6592900ef2a751e8907a72557b0ba794e0d6f1252c374_arguments": "(reversed rest)"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/test_handcalc_b.clsp b/resources/tests/game-referee-in-cl21/test_handcalc_b.clsp new file mode 100644 index 000000000..674bbd414 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_handcalc_b.clsp @@ -0,0 +1,377 @@ + +(mod () + (include *standard-cl-21*) + (include deep_compare.clinc) + (include assert.clinc) + (include reverse.clinc) + (include prepend.clinc) + (include map.clinc) + (include filtermap.clinc) + (include slice.clinc) + (include print.clinc) + (include handcalc.clinc) + + (defun runtests_inner ((myfunc firstarg secondarg . remaining)) + (assign-lambda + firstval (handcalc firstarg) + secondval (handcalc secondarg) + (assert + (a myfunc (list firstval secondval)) + (deep= firstval (handcalc (reverse firstarg))) + (deep= secondval (handcalc (reverse secondarg))) + (if remaining + (runtests_inner remaining) + 0 + ) + ) + ) + ) + + (defun runtests tests (if tests (runtests_inner tests) ())) + + ;; Join these up when large application bug is fixed. + (runtests + ; all beats both emerge over and measure higher + ; straight flush with higher kicker ties + ; A1 K1 Q1 J1 T1 91 = A1 K1 Q1 J1 T1 81 + deep= + (list (c 12 1) (c 11 1) (c 14 1) (c 13 1) (c 10 1) (c 9 1)) + (list (c 12 1) (c 11 1) (c 14 1) (c 13 1) (c 10 1) (c 8 1)) + ; straight flushes of different suits tie + ; A1 K1 Q1 J1 T1 = A2 K2 Q2 J2 T2 + deep= + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 10 1)) + (list (c 14 2) (c 13 2) (c 12 2) (c 11 2) (c 10 2)) + ; higher straight flush beats lower straight flush + ; A1 K1 Q1 J1 T1 > 61 51 41 31 21 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 10 1)) + (list (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + ; A1 K1 Q1 J1 T1 91 = A1 K1 Q1 J1 T1 + deep= + (list (c 12 1) (c 11 1) (c 14 1) (c 13 1) (c 10 1) (c 9 1)) + (list (c 14 2) (c 11 2) (c 10 2) (c 13 2) (c 12 2)) + ; lower (2-6) straight flush beats ace to four straight flush + ; 61 51 41 31 21 > A2 52 42 32 22 + deep> + (list (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 14 2) (c 5 2) (c 4 2) (c 3 2) (c 2 2)) + ; A1 61 51 41 31 21 = 61 51 41 31 21 + deep= + (list (c 14 1) (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 6 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + ; ace to four straight flush with higher kicker ties + ; A2 52 42 32 22 61 = A1 51 41 31 21 71 + deep= + (list (c 14 2) (c 5 2) (c 4 2) (c 3 2) (c 2 2) (c 6 1)) + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1) (c 7 1)) + ; ace to four straight flushes of different suits tie + ; A1 51 41 31 21 = A2 52 42 32 22 + deep= + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 14 2) (c 5 2) (c 4 2) (c 3 2) (c 2 2)) + ; ace to four straight flush beats four of a kind + ; A1 51 41 31 21 > K1 K2 K3 K4 J1 + deep> + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 11 1)) + ; A1 A2 A3 A4 51 41 31 21 = A1 51 41 31 21 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + (list (c 14 1) (c 5 1) (c 4 1) (c 3 1) (c 2 1)) + ; four of a kind with higher kicker wins + ; K1 K2 K3 K4 Q1 > K1 K2 K3 K4 J1 + deep> + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 12 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 11 1)) + ; K1 K2 K3 K4 T1 91 = K1 K2 K3 K4 T1 + deep= + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 10 1) (c 9 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 10 1)) + ; four of a kind with higher second kicker ties + ; K1 K2 K3 K4 Q1 J1 = K1 K2 K3 K4 Q1 T1 + deep= + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 12 1) (c 11 1)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 12 1) (c 10 1)) + ; higher four of a kind beats lower four of a kind + ; K1 K2 K3 K4 21 > 31 32 33 34 A1 + deep> + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 2 1)) + (list (c 3 1) (c 3 2) (c 3 3) (c 3 4) (c 14 1)) + ; K1 K2 K3 K4 31 32 33 34 = K1 K2 K3 K4 32 + deep= + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 3 1) (c 3 2) (c 3 3) (c 3 4)) + (list (c 13 1) (c 13 2) (c 13 3) (c 13 4) (c 3 2)) + ; four of a kind beats full house + ; 21 22 23 24 31 > A1 A2 A3 K1 K2 + deep> + (list (c 2 1) (c 2 2) (c 2 3) (c 2 4) (c 3 1)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2)) + ; 21 22 23 24 A1 A2 A3 = 21 22 23 24 A2 + deep= + (list (c 2 1) (c 2 2) (c 2 3) (c 2 4) (c 14 1) (c 14 2) (c 14 3)) + (list (c 2 1) (c 2 2) (c 2 3) (c 2 4) (c 14 2)) + ; full house with higher set wins + ; 51 52 53 21 22 > 31 32 33 71 72 + deep> + (list (c 5 1) (c 5 2) (c 5 3) (c 2 1) (c 2 2)) + (list (c 3 1) (c 3 2) (c 3 3) (c 7 1) (c 7 2)) + ; A1 A2 A3 K1 K2 K3 = A1 A2 A3 K1 K2 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2) (c 13 3)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2)) + ; full house with same set and higher pair wins + ; 51 52 53 41 42 > 51 52 53 31 32 + deep> + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2)) + (list (c 5 1) (c 5 2) (c 5 3) (c 3 1) (c 3 2)) + ; A1 A2 A3 K1 K2 51 52 = A1 A2 A3 K1 K2 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2) (c 5 1) (c 5 2)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 13 2)) + ; full house ties with two sets + ; 51 52 53 41 42 A1 = 51 52 53 41 42 43 + deep= + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2) (c 14 1)) + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2) (c 4 3)) + ; full house beats flush + ; 51 52 53 41 42 > A1 Q1 T1 81 71 + deep> + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2)) + (list (c 14 1) (c 12 1) (c 10 1) (c 8 1) (c 7 1)) + ; 51 52 53 41 42 A1 K1 Q1 = 51 52 53 41 42 + deep= + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2) (c 14 1) (c 13 1) (c 12 1)) + (list (c 5 1) (c 5 2) (c 5 3) (c 4 1) (c 4 2)) + ; higher flush beats lower flush + ; A1 61 51 41 31 > K1 Q1 J1 T1 81 + deep> + (list (c 14 1) (c 6 1) (c 5 1) (c 4 1) (c 3 1)) + (list (c 13 1) (c 12 2) (c 11 1) (c 10 1) (c 8 1)) + ; A1 K1 Q1 J1 81 71 = A1 K1 Q1 J1 81 + deep= + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 8 1) (c 7 1)) + (list (c 14 1) (c 13 1) (c 12 1) (c 11 1) (c 8 1)) + ; flush with higher second card wins + ; A1 K1 51 41 31 > A1 Q1 J1 T1 91 + deep> + (list (c 14 1) (c 13 1) (c 5 1) (c 4 1) (c 3 1)) + (list (c 14 1) (c 12 2) (c 11 1) (c 10 1) (c 9 1)) + ; flush with higher third card wins + ; A1 K1 Q1 41 31 > A1 K1 J1 T1 91 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 4 1) (c 3 1)) + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 9 1)) + ; flush with higher fourth card wins + ; A1 K1 Q1 T1 21 > A1 K1 Q1 91 81 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 10 1) (c 2 1)) + (list (c 14 1) (c 13 1) (c 12 1) (c 9 1) (c 8 1)) + ; flush with higher fifth card wins + ; A1 K1 Q1 T1 81 > A1 K1 Q1 T1 71 + deep> + (list (c 14 1) (c 13 1) (c 12 1) (c 10 1) (c 8 1)) + (list (c 14 1) (c 13 1) (c 12 1) (c 10 1) (c 7 1)) + ; flushes of different suits tie + ; A1 K1 J1 T1 81 = A2 K2 J2 T2 82 + deep= + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 8 1)) + (list (c 14 2) (c 13 2) (c 11 2) (c 10 2) (c 8 2)) + ; same flush with higher sixth card ties + ; A1 K1 J1 T1 81 71 = A1 K1 J1 T1 81 61 + deep= + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 8 1) (c 7 1)) + (list (c 14 1) (c 13 1) (c 11 1) (c 10 1) (c 8 1) (c 6 1)) + ; flush beats straight + ; 71 61 51 41 21 > A1 K2 Q3 J4 T1 + deep> + (list (c 7 1) (c 6 1) (c 5 1) (c 4 1) (c 2 1)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1)) + ; A1 K2 Q3 J4 T1 81 71 61 = A1 T1 81 71 61 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 8 1) (c 7 1) (c 6 1)) + (list (c 14 1) (c 10 1) (c 8 1) (c 7 1) (c 6 1)) + ; straight with higher kicker ties + ; A1 K2 Q3 J4 T1 92 = A1 K2 Q3 J4 T1 22 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 9 2)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 2 2)) + ; straights of different suits tie + ; A1 K2 Q3 J4 T1 = A2 K3 Q4 J1 T2 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1)) + (list (c 14 2) (c 13 3) (c 12 4) (c 11 1) (c 10 2)) + ; higher straight beats lower straight + ; A1 K2 Q3 J4 T1 > 61 52 43 34 21 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1)) + (list (c 6 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + ; A1 K2 Q3 J4 T1 92 83 = A1 K2 Q3 J4 T1 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 10 1) (c 9 2) (c 8 3)) + (list (c 14 2) (c 13 3) (c 12 4) (c 11 1) (c 10 2)) + ; lower (2-6) straight beats ace to four straight + ; 61 52 43 34 21 > A1 52 43 34 21 + deep> + (list (c 6 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + ; A1 62 53 44 31 22 = 62 53 44 31 22 + deep= + (list (c 14 1) (c 6 2) (c 5 3) (c 4 4) (c 3 1) (c 2 2)) + (list (c 6 2) (c 5 3) (c 4 4) (c 3 1) (c 2 2)) + ; ace to four straight with higher kicker ties + ; A1 52 43 34 21 K2 = A1 52 43 34 21 72 + deep= + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1) (c 13 2)) + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1) (c 7 2)) + ; ace to fours of different suits tie + ; A1 52 43 34 21 = A2 53 44 31 22 + deep= + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 2) (c 5 3) (c 4 4) (c 3 1) (c 2 2)) + ; ace to four straight beats set + ; A1 52 43 34 21 > A1 A2 A3 K1 Q2 + deep> + (list (c 14 1) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 14 2) (c 14 3) (c 13 1) (c 12 2)) + ; A1 A2 A3 52 43 34 21 = A1 52 43 34 21 + deep= + (list (c 14 1) (c 14 2) (c 14 3) (c 5 2) (c 4 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 5 2) (c 4 3) (c 3 2) (c 2 1)) + ; higher set wins + ; 71 72 73 34 21 > 51 52 53 A4 K1 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 3 4) (c 2 1)) + (list (c 5 1) (c 5 2) (c 5 3) (c 14 4) (c 13 1)) + ; set with higher first kicker wins + ; 71 72 73 A1 22 > 71 72 73 K1 Q2 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 2 2)) + (list (c 7 1) (c 7 2) (c 7 3) (c 13 1) (c 12 2)) + ; 71 72 73 A1 K2 J3 54 43 = 71 72 73 A1 K2 + deep= + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2) (c 11 3) (c 5 4) (c 4 3)) + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2)) + ; set with higher second kicker wins + ; 71 72 73 A1 K2 > 71 72 73 A1 Q2 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2)) + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 12 2)) + ; set with higher third kicker ties + ; 71 72 73 A1 K2 Q3 = 71 72 73 A1 K2 J3 + deep= + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2) (c 12 3)) + (list (c 7 1) (c 7 2) (c 7 3) (c 14 1) (c 13 2) (c 11 3)) + ; set beats two pair + ; 71 72 73 34 21 > A1 A2 K3 K4 Q1 + deep> + (list (c 7 1) (c 7 2) (c 7 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 14 2) (c 13 3) (c 13 4) (c 12 1)) + ; two pair with higher high pair wins + ; K1 K2 33 34 21 > Q1 Q2 J3 J4 A1 + deep> + (list (c 13 1) (c 13 2) (c 3 3) (c 3 4) (c 2 1)) + (list (c 12 1) (c 12 2) (c 11 3) (c 11 4) (c 14 1)) + ; A1 A2 K1 K2 J1 J2 = A1 A2 K1 K2 J3 + deep= + (list (c 14 1) (c 14 2) (c 13 1) (c 13 2) (c 11 1) (c 11 2)) + (list (c 14 1) (c 14 2) (c 13 1) (c 13 2) (c 11 3)) + ; two pair with tied higher pair and higher lower pair wins + ; K1 K2 71 72 23 > K1 K2 63 64 A1 + deep> + (list (c 13 1) (c 13 2) (c 7 1) (c 7 2) (c 2 3)) + (list (c 13 1) (c 13 2) (c 6 3) (c 6 4) (c 14 1)) + ; two pair with higher kicker wins + ; K1 K2 Q3 Q4 J1 > K1 K2 Q3 Q4 T1 + deep> + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 11 1)) + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 10 1)) + ; K1 K2 Q3 Q4 A1 T1 92 63 = K1 K2 Q3 Q4 A1 + deep= + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 14 1) (c 10 1) (c 9 2) (c 6 3)) + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 14 1)) + ; two pair with higher second kicker ties + ; K1 K2 Q3 Q4 J1 T2 = K1 K2 Q3 Q4 J1 92 + deep= + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 11 1) (c 10 2)) + (list (c 13 1) (c 13 2) (c 12 3) (c 12 4) (c 11 1) (c 9 2)) + ; two pair beats pair + ; 41 42 33 34 21 > A1 A2 K3 Q4 J1 + deep> + (list (c 4 1) (c 4 2) (c 3 3) (c 3 4) (c 2 1)) + (list (c 14 1) (c 14 2) (c 13 3) (c 12 4) (c 11 1)) + ; higher pair wins + ; 71 72 53 44 31 > 61 62 A3 K4 Q1 + deep> + (list (c 7 1) (c 7 2) (c 5 3) (c 4 4) (c 3 1)) + (list (c 6 1) (c 6 2) (c 14 3) (c 13 4) (c 12 1)) + ; tied pair with higher first kicker wins + ; 91 92 A3 34 21 > 91 92 K3 Q4 J1 + deep> + (list (c 9 1) (c 9 2) (c 14 3) (c 3 4) (c 2 1)) + (list (c 9 1) (c 9 2) (c 13 3) (c 12 4) (c 11 1)) + ; 21 22 A1 Q2 J3 94 81 = 21 22 A1 Q2 J3 + deep= + (list (c 2 1) (c 2 2) (c 14 1) (c 12 2) (c 11 3) (c 9 4) (c 8 1)) + (list (c 2 1) (c 2 2) (c 14 1) (c 12 2) (c 11 3)) + ; tied pair with higher second kicker wins + ; 91 92 A3 K4 21 > 91 92 A3 Q4 J1 + deep> + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 2 1)) + (list (c 9 1) (c 9 2) (c 14 3) (c 12 4) (c 11 1)) + ; tied pair with higher third kicker wins + ; 91 92 A3 K4 Q1 > 91 92 A3 K4 J1 + deep> + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 12 1)) + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 11 1)) + ; tied pair with higher fourth kicker ties + ; 91 92 A3 K4 Q1 J2 = 91 92 A3 K4 Q1 T2 + deep= + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 12 1) (c 11 2)) + (list (c 9 1) (c 9 2) (c 14 3) (c 13 4) (c 12 1) (c 10 2)) + ; pair beats high card + ; 21 22 33 44 51 > A1 Q2 J3 T4 91 + deep> + (list (c 2 1) (c 2 2) (c 3 3) (c 4 4) (c 5 1)) + (list (c 14 1) (c 12 2) (c 11 3) (c 10 4) (c 9 1)) + ; higher high card wins + ; A1 22 33 44 61 > K1 Q2 J3 T4 81 + deep> + (list (c 14 1) (c 2 2) (c 3 3) (c 4 4) (c 6 1)) + (list (c 13 1) (c 12 2) (c 11 3) (c 10 4) (c 8 1)) + ; A1 K2 J3 T4 81 72 53 = A1 K2 J3 T4 81 + deep= + (list (c 14 1) (c 13 2) (c 11 3) (c 10 4) (c 8 1) (c 7 2) (c 5 3)) + (list (c 14 1) (c 13 2) (c 11 3) (c 10 4) (c 8 1)) + ; higher second card wins + ; A1 K2 23 34 41 > A1 Q2 J3 T4 91 + deep> + (list (c 14 1) (c 13 2) (c 2 3) (c 3 4) (c 4 1)) + (list (c 14 1) (c 12 2) (c 11 3) (c 10 4) (c 9 1)) + ; higher third card wins + ; A1 K2 Q3 24 41 > A1 K2 J3 T4 91 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 2 4) (c 4 1)) + (list (c 14 1) (c 13 2) (c 11 3) (c 10 4) (c 9 1)) + ; higher fourth card wins + ; A1 K2 Q3 J4 31 > A1 K2 Q3 T4 91 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 3 1)) + (list (c 14 1) (c 13 2) (c 12 3) (c 10 4) (c 9 1)) + ; higher fifth card wins + ; A1 K2 Q3 J4 91 > A1 K2 Q3 J4 81 + deep> + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 8 1)) + ; higher sixth card ties + ; A1 K2 Q3 J4 91 22 = A1 K2 Q3 J4 91 82 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1) (c 2 2)) + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1) (c 8 2)) + ; high cards of different suits ties + ; A1 K2 Q3 J4 91 = A2 K3 Q4 J1 92 + deep= + (list (c 14 1) (c 13 2) (c 12 3) (c 11 4) (c 9 1)) + (list (c 14 2) (c 13 3) (c 12 4) (c 11 1) (c 9 2)) + ) +) diff --git a/resources/tests/game-referee-in-cl21/test_library_basics.py b/resources/tests/game-referee-in-cl21/test_library_basics.py new file mode 100644 index 000000000..926ddde7a --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_library_basics.py @@ -0,0 +1,165 @@ +import os +import pytest +import random +from itertools import permutations +from typing import List +#from hsms.streamables.program import Program +#from steprun import diag_run_clvm, compile_module_with_symbols +#from lib.program import Program +from pathlib import Path +from clvm_rs import Program +from lib.steprun import diag_run_clvm, compile_module_with_symbols +from clvm_tools_rs import get_version + +print(f"clvm_tools_rs version is {get_version()}") +#include_dirs = os.getcwd() +include_dirs = [Path(__file__).parent, Path(__file__).parent.parent / "lib"] +Program.set_run_unsafe_max_cost(11000000000) + +print(f"XXX: {include_dirs}") +compile_module_with_symbols(include_dirs, 'smoke_test_deep_compare.clsp') +compare_program = Program.from_bytes(bytes.fromhex(open('smoke_test_deep_compare.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'smoke_test_sort.clsp') +sort_program = Program.from_bytes(bytes.fromhex(open('smoke_test_sort.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'test_sort.clsp') +test_sort_program = Program.from_bytes(bytes.fromhex(open('test_sort.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'test_permutations.clsp') +test_permutations_program = Program.from_bytes(bytes.fromhex(open('test_permutations.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'test_reverse.clsp') +test_reverse_program = Program.from_bytes(bytes.fromhex(open('test_reverse.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'test_prepend.clsp') +test_prepend_program = Program.from_bytes(bytes.fromhex(open('test_prepend.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'test_range.clsp') +test_range_program = Program.from_bytes(bytes.fromhex(open('test_range.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'smoke_test_permutations.clsp') +smoke_test_permutations_program = Program.from_bytes(bytes.fromhex(open('smoke_test_permutations.clvm.hex').read())) + +compile_module_with_symbols(include_dirs, 'test_handcalc.clsp') +test_handcalc_program = Program.from_bytes(bytes.fromhex(open('test_handcalc.clvm.hex').read())) + +def as_atom_list(prg: Program) -> List[bytes]: + """ + Pretend `prg` is a list of atoms. Return the corresponding + python list of atoms. + + At each step, we always assume a node to be an atom or a pair. + If the assumption is wrong, we exit early. This way we never fail + and always return SOMETHING. + """ + items = [] + obj = prg + while True: + pair = obj.pair + if pair is None: + break + atom = pair[0].atom + if atom is None: + break + items.append(atom) + obj = pair[1] + return items + +def test_smoke_compare(): + compare_program.run(Program.to([])) + +def test_handcalc(): + diag_run_clvm(test_handcalc_program, Program.to([]), 'test_handcalc.sym') + +def proper_list_inner(result,cl): + if hasattr(cl, 'pair') and cl.pair is not None: + result.append(cl.pair[0]) + return proper_list_inner(result,cl.pair[1]) + else: + return result + +def proper_list(cl): + result = [] + return proper_list_inner(result,cl) + +def int_list(cl): + return [Program.to(x).as_int() for x in as_atom_list(Program.to(cl))] + +def de_none_list(l): + return [x if x is not None else [] for x in l] + +def with_random_lists(n,f): + for length in range(n): # 0-10 length + for i in range(1 + (3 * length)): # A few orders each + orig_list = [random.randint(0,100) for x in range(length)] + f(orig_list) + +def test_prepend(): + for length1 in range(5): + list_1 = list(range(length1)) + for length2 in range(length1): + prepend_result = test_prepend_program.run([Program.to(list_1[:length2]),Program.to(list_1[length2:])]) + assert list_1 == int_list(prepend_result) + +def test_reverse(): + def test_reverse_list(l): + rev_args = Program.to([l]) + reversed_result = Program.to(list(reversed(l))) + reversed_by_prog = test_reverse_program.run(rev_args) + assert reversed_result == reversed_by_prog + + with_random_lists(10,test_reverse_list) + +def test_range(): + for length in range(10): + want_list = list(range(length)) + result = test_range_program.run(Program.to([length])) + assert want_list == result + +def do_test_permutations_of_size_n(n): + try_list = [random.randint(0,100) for x in range(n)] + want_set = list([list(v) for v in sorted(permutations(try_list))]) + listed_result = smoke_test_permutations_program.run(Program.to([try_list])) + pl = proper_list(listed_result) + perms_result = sorted([int_list(x) for x in de_none_list(pl)]) + assert want_set == perms_result + +def test_permutations_0(): + do_test_permutations_of_size_n(0) + +def test_permutations_1(): + do_test_permutations_of_size_n(1) + +def test_permutations_2(): + n = 2 + all_a_string = 0x616161616161 + all_b_string = 0x626262626262 + for try_list in [[all_a_string,all_b_string], [all_b_string,all_a_string]]: + want_set = list([list(v) for v in sorted(permutations(try_list))]) + listed_result = diag_run_clvm(smoke_test_permutations_program, Program.to([try_list]), 'smoke_test_permutations.sym') + pl = proper_list(listed_result) + perms_result = sorted([int_list(x) for x in de_none_list(pl)]) + assert want_set == perms_result + +def test_chialisp_sort_program(): + diag_run_clvm(test_sort_program, Program.to([]), 'test_sort.sym') + +def test_permutations_n(): + for i in range(3,6): + do_test_permutations_of_size_n(i) + +def test_chialisp_permutations_program(): + diag_run_clvm(test_permutations_program, Program.to([3, 5]), 'test_permutations.sym') + +def test_smoke_sort(): + for length in range(7): # 0-7 length + for i in range(1 + (3 * length)): # A few orders each + orig_list = [random.randint(0,100) for x in range(length)] + sort_args = Program.to([orig_list]) + sorted_list = Program.to(sorted(orig_list)) + sort_res = sort_program.run(sort_args) + assert sort_res == sorted_list + +if __name__ == '__main__': + test_smoke_sort() diff --git a/resources/tests/game-referee-in-cl21/test_permutations.clsp b/resources/tests/game-referee-in-cl21/test_permutations.clsp new file mode 100644 index 000000000..d5b6d125f --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_permutations.clsp @@ -0,0 +1,63 @@ +(mod (M N) + (include *standard-cl-21*) + (include prepend.clinc) + (include reverse.clinc) + (include map.clinc) + (include len.clinc) + (include range.clinc) + (include sort.clinc) + (include assert.clinc) + (include deep_compare.clinc) + (include permutations.clinc) + (include last.clinc) + (include busy.clinc) + (include all-in-list.clinc) + (include print.clinc) + + (defun ! (x) + (if x + (* x (! (- x 1))) + 1 + ) + ) + (defun no_repeats_inner ((first . remainder)) + (if remainder + (if (deep= first (f remainder)) + 0 + (no_repeats_inner remainder) + ) + 1 + ) + ) + (defun no_repeats (mylist) + (if mylist + (no_repeats_inner (sort (lambda (a b) (= (deep_compare a b) -1)) mylist)) + 1 + ) + ) + (assert + ;; Is permutations expected to collapse equal alternatives when two of + ;; the items to shuffle are equal? + (= (* (! M) 4) (len (permutations (c 0 (range M))))) + (busy + (lambda (listlen) + (assign + mylist (range listlen) + permed (permutations mylist) + (assert + (= (len permed) (! listlen)) + ;; ensure we didn't produce any permutations that have + ;; repeated elements in them, which would indicate that + ;; the permutation function misbehaved + (all-in-list (map (lambda (L) (no_repeats L)) permed)) + (no_repeats permed) + ) + ) + ) + (reverse (range N)) + 1 + ) + (deep= (permutations 0) (q ())) + 0 + ) +) diff --git a/resources/tests/game-referee-in-cl21/test_permutations.clvm.hex.reference b/resources/tests/game-referee-in-cl21/test_permutations.clvm.hex.reference new file mode 100644 index 000000000..9758d6fd7 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_permutations.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ffff03ffff09ffff12ffff02ff66ffff04ff02ffff04ff05ff80808080ffff010480ffff02ff48ffff04ff02ffff04ffff02ff4affff04ff02ffff04ffff04ffff0180ffff02ff58ffff04ff02ffff04ff05ff8080808080ff80808080ff8080808080ffff01ff02ffff01ff02ffff03ffff02ff5affff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200fe80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ffff02ff50ffff04ff02ffff04ffff02ff58ffff04ff02ffff04ff0bff80808080ff80808080ffff04ffff0101ff808080808080ffff01ff02ffff01ff02ffff03ffff02ff42ffff04ff02ffff04ffff02ff4affff04ff02ffff04ffff0180ff80808080ffff04ffff01ff8080ff8080808080ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ffff04ffff01ffffffffffff02ffff03ff05ffff01ff02ffff01ff04ffff05ff0580ffff02ff40ffff04ff02ffff04ffff06ff0580ffff04ff0bff808080808080ff0180ffff01ff02ffff010bff018080ff0180ff02ffff03ff0bffff01ff02ffff01ff02ff60ffff04ff02ffff04ffff04ffff05ff0b80ff0580ffff04ffff06ff0b80ff8080808080ff0180ffff01ff02ffff0105ff018080ff0180ffff02ff60ffff04ff02ffff04ff80ffff04ff05ff8080808080ff02ffff03ff0bffff01ff02ffff01ff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff02ff70ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff80808080808080ff0180ffff01ff02ffff0117ff018080ff0180ffffff02ffff03ff05ffff01ff02ffff01ff10ffff0101ffff02ff48ffff04ff02ffff04ffff06ff0580ff8080808080ff0180ffff01ff02ffff01ff0180ff018080ff0180ff02ffff03ffff09ff05ff0b80ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff04ff05ffff02ff68ffff04ff02ffff04ffff10ff05ffff010180ffff04ff0bff808080808080ff018080ff0180ffff02ff68ffff04ff02ffff04ff80ffff04ff05ff8080808080ff02ffff03ff05ffff01ff02ffff01ff02ff78ffff04ff02ffff04ffff06ff0580ffff04ff17ffff04ffff04ffff05ff0580ff0b80ff808080808080ff0180ffff01ff02ffff01ff06ff0380ff018080ff0180ffffffff02ff78ffff04ff02ffff04ff05ffff01ff80ff8080808080ff02ffff03ffff20ff0b80ffff01ff02ffff01ff02ff40ffff04ff02ffff04ffff02ff50ffff04ff02ffff04ff2fff80808080ffff04ff17ff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff20ff1780ffff01ff02ffff01ff02ff40ffff04ff02ffff04ffff02ff50ffff04ff02ffff04ff2fff80808080ffff04ff0bff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff02ff05ffff04ffff05ff0b80ffff04ffff05ff1780ffff0180808080ffff01ff02ffff01ff02ff64ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ffff04ffff04ffff05ff0b80ff2f80ff80808080808080ff0180ffff01ff02ffff01ff02ff64ffff04ff02ffff04ff05ffff04ff0bffff04ffff06ff1780ffff04ffff04ffff05ff1780ff2f80ff80808080808080ff018080ff0180ff018080ff0180ff018080ff0180ffff02ff64ffff04ff02ffff04ff05ffff04ff0bffff04ff17ffff01ff80808080808080ff02ffff03ff0bffff01ff02ffff01ff02ffff03ffff06ff0b80ffff01ff02ffff01ff02ff4cffff04ff02ffff04ffff06ff0180ffff04ffff02ff44ffff04ff02ffff04ff0bff80808080ff8080808080ff0180ffff01ff02ffff010bff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ffffff02ff6cffff04ff02ffff04ff03ffff04ffff02ff74ffff04ff02ffff04ff09ffff04ff13ff8080808080ffff04ffff02ff74ffff04ff02ffff04ff09ffff04ff2bff8080808080ff808080808080ff02ff54ffff04ff02ffff04ff11ffff04ff0bffff04ff17ff808080808080ffff02ffff03ffff07ff0580ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff02ff7cffff04ff02ffff04ffff06ff0180ffff04ffff02ff5cffff04ff02ffff04ffff05ff0580ffff04ffff05ff0b80ff8080808080ff8080808080ff0180ffff01ff02ffff01ff0101ff018080ff0180ff0180ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff0181ffff0180ffff01ff02ffff01ff02ffff03ffff15ff05ff0b80ffff01ff02ffff01ff0101ff0180ffff01ff02ffff01ff11ffff0180ffff15ff0bff058080ff018080ff0180ff018080ff0180ff018080ff0180ff02ffff03ff0bffff01ff02ffff010bff0180ffff01ff02ffff01ff02ff5cffff04ff02ffff04ffff06ff0980ffff04ffff06ff1580ff8080808080ff018080ff0180ffffffffff09ffff02ff5cffff04ff02ffff04ff05ffff04ff0bff8080808080ff8080ff02ffff03ffff20ff0b80ffff01ff02ffff0117ff0180ffff01ff02ffff01ff02ff52ffff04ff02ffff04ffff06ff0180ffff04ffff05ff0b80ffff04ffff06ff0b80ff808080808080ff018080ff0180ffff02ff70ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff7280ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff04ff0bff808080ffff01ff01808080ff80808080ffff04ffff02ff4affff04ff02ffff04ffff02ff40ffff04ff02ffff04ff09ffff04ff17ff8080808080ff80808080ffff04ffff02ff62ffff04ff02ffff04ffff04ff0bff0980ffff04ff17ffff04ff2dff808080808080ff808080808080ff04ff09ff0b80ffffff02ffff03ff05ffff01ff02ffff01ff02ff62ffff04ff02ffff04ffff0180ffff04ff05ffff04ffff0180ff808080808080ff0180ffff01ff02ffff01ff01ff8080ff018080ff0180ff02ffff03ff0dffff01ff02ffff01ff02ff6affff04ff02ffff04ff0dff80808080ff0180ffff01ff02ffff0109ff018080ff0180ffff02ffff03ff0bffff01ff02ffff01ff02ff6affff04ff02ffff04ffff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff04ffff02ff5affff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff808080808080ffff01808080ff80808080ff0180ffff01ff02ffff0117ff018080ff0180ff02ffff03ff05ffff01ff02ffff01ff04ffff04ffff0101ffff05ff058080ffff02ff7affff04ff02ffff04ffff06ff0580ff8080808080ff0180ffff01ff02ffff01ff0180ff018080ff0180ffffffff02ffff04ffff0122ffff02ff7affff04ff02ffff04ff05ff8080808080ff8080ff02ffff03ff05ffff01ff02ffff01ff12ff05ffff02ff66ffff04ff02ffff04ffff11ff05ffff010180ff8080808080ff0180ffff01ff02ffff01ff0101ff018080ff0180ffff02ffff03ff0dffff01ff02ffff01ff02ffff03ffff02ff42ffff04ff02ffff04ff09ffff04ffff05ff0d80ff8080808080ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff02ff56ffff04ff02ffff04ff0dff80808080ff018080ff0180ff0180ffff01ff02ffff01ff0101ff018080ff0180ff02ffff03ff05ffff01ff02ffff01ff02ff56ffff04ff02ffff04ffff02ff74ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff4e80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ff05ff8080808080ff80808080ff0180ffff01ff02ffff01ff0101ff018080ff0180ffffff09ffff02ff5cffff04ff02ffff04ff0bffff04ff17ff8080808080ffff0181ff80ff02ff5effff04ff02ffff04ff03ffff04ffff02ff4affff04ff02ffff04ff0bff80808080ff8080808080ffff02ffff03ffff09ffff02ff48ffff04ff02ffff04ff0bff80808080ffff02ff66ffff04ff02ffff04ff29ff8080808080ffff01ff02ffff01ff02ffff03ffff02ff46ffff04ff02ffff04ffff02ff70ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff8200be80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ff0bffff04ffff0180ff808080808080ff80808080ffff01ff02ffff01ff02ff76ffff04ff02ffff04ff0bff80808080ff0180ffff01ff02ffff01ff0880ff018080ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ffff02ff76ffff04ff02ffff04ff0bff80808080ff02ff6effff04ff02ffff04ffff04ff80ffff04ff0bff808080ffff04ffff02ff58ffff04ff02ffff04ff0bff80808080ff8080808080ff018080 diff --git a/resources/tests/game-referee-in-cl21/test_permutations.sym b/resources/tests/game-referee-in-cl21/test_permutations.sym new file mode 100644 index 000000000..d28b3e38a --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_permutations.sym @@ -0,0 +1 @@ +{"42e1f82cf8542a5f31de7edcd596df9a08e48bcf0aa637f866be044a0c96841f_left_env": "1", "78956ca35a10cbc40b04bd0a14ca12faae5f292d70b9e072da61ef82f3677522": "no_repeats_inner", "9acbd39570d133bd1318e96254949ed3f9d84fb6796324d393b68b898c4999c8_left_env": "1", "ae7e4ed6da640a7bd1e9ef3b1d683440bf9297b217bf0743328f9ae54bbc5b53_arguments": "(reversed rest)", "85c5c955b32be4fef52509b26fe07ce1c3086244572fde3f3bc47636c20f86de": "deep_compare", "93ebb2292470d39d1a556d1294e5da2de17b55aa7d6b591cb885754273777746": "permutations_inner", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708_left_env": "1", "19f60e08ef187986cf06532ba696b5b91efeb5c043318b9e6b731f1cd80adf79": "last_inner", "85c5c955b32be4fef52509b26fe07ce1c3086244572fde3f3bc47636c20f86de_arguments": "(a b)", "1c9d01e44e9c5f35a282358422033e4e960609d9f5ef0efc7c7b48bf311208ff_left_env": "1", "0ebdfa3c097ba5b3ef741e8a33b6af2f741209b3e29d94c1c55deb599e4c41d7_left_env": "1", "657a0532257810b1c203d201026eccd16b845b156e25600b57a7ed5429c59682": "no_repeats", "858e89b0997add55f954ceda806d5a77e9914f6d31bed3dedd1e9b67581ee202_arguments": "(@ everything (rest aggl aggr))", "93ebb2292470d39d1a556d1294e5da2de17b55aa7d6b591cb885754273777746_left_env": "1", "42e1f82cf8542a5f31de7edcd596df9a08e48bcf0aa637f866be044a0c96841f_arguments": "(myless A B agg)", "0ebdfa3c097ba5b3ef741e8a33b6af2f741209b3e29d94c1c55deb599e4c41d7": "busy", "e23db45dac9015daa0e667d0d846c354b2b9d9aaa3faedb560854d8662f2e238_arguments": "((() listlen) mylist)", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/test_permutations.clsp", "0871deba30de6d95a0b04a45e6e036162f8abe844ae21b31f7d7cd518845fe9c": "range_inner", "ae7e4ed6da640a7bd1e9ef3b1d683440bf9297b217bf0743328f9ae54bbc5b53": "reverse_inner", "93ebb2292470d39d1a556d1294e5da2de17b55aa7d6b591cb885754273777746_arguments": "(pre post agg)", "a91dc4bec838278871d09bbe4a7206242b852404596d71f8e7962df872da447e_arguments": "(L)", "9fbce34b16a7c4618d5816009b42c6e661cb7efbcaf90314a2f840362f175793": "lambda_$_305", "ae7e4ed6da640a7bd1e9ef3b1d683440bf9297b217bf0743328f9ae54bbc5b53_left_env": "1", "5087819cc352ab86b0b64eebf0a29725fb6306291a42ac51f17253919f7b899c_left_env": "1", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708": "lambda_$_304", "657a0532257810b1c203d201026eccd16b845b156e25600b57a7ed5429c59682_left_env": "1", "9242b5b6b2f9c8d952001db1fbcffc4b477638bf957f6a5dd3a2cc742919c977_arguments": "(vals)", "6c37e5a9ce5bcf0ae750dd4bb1db5898f49d63eadcdf27fbe04c787362459f5d_arguments": "(((() listlen) mylist) permed)", "9acbd39570d133bd1318e96254949ed3f9d84fb6796324d393b68b898c4999c8_arguments": "(F L R)", "1443073ed355cb83607d1c2a6d5612f28a038d3887fa750de973e6aa0ed83f08_arguments": "(myless a b)", "85c5c955b32be4fef52509b26fe07ce1c3086244572fde3f3bc47636c20f86de_left_env": "1", "5087819cc352ab86b0b64eebf0a29725fb6306291a42ac51f17253919f7b899c": "letbinding_$_300", "ae16459987997a1c9872031a855e1dfe8138fde4d1d5b9a5f188d3292ea97565": "sort", "2c82355dea6595dde07e0d5e05aa96b89052c8b97d0e0c868356df4914f2fe68_left_env": "1", "2e3d155705dec0e05e5d4c87c93395ffe98fffe9f21542faca3c5c8751a18047": "deep=", "483fcccf52dd01c7be19ebb1f6ec50a0c235873236a8ab545236771af634b706": "lambda_$_298", "ae16459987997a1c9872031a855e1dfe8138fde4d1d5b9a5f188d3292ea97565_arguments": "(myless mylist)", "a0c582b1c6b05c8a3f5d5d7afd51404b874bb63f21ef73f31c383007fce81e87": "prepend", "7381ec0d3e15950009d8b999a34b828e9964b79470270e7ed6c5409cf2f69bb9_left_env": "1", "6c37e5a9ce5bcf0ae750dd4bb1db5898f49d63eadcdf27fbe04c787362459f5d": "letbinding_$_306", "6c37e5a9ce5bcf0ae750dd4bb1db5898f49d63eadcdf27fbe04c787362459f5d_left_env": "1", "657a0532257810b1c203d201026eccd16b845b156e25600b57a7ed5429c59682_arguments": "(mylist)", "6eb64b0043f60821f8c78916633c5d028164853a0ef677c0b35666673f4163ba_left_env": "1", "02c081d44a600583556699febd4e3bc2eb117b61bd5cfc612c2bf091c952fa0a_left_env": "1", "61f9f86f9df2ce0986b2ae605b74b6111f767e89b83729af6f34110bf65bdeb2": "!", "02c081d44a600583556699febd4e3bc2eb117b61bd5cfc612c2bf091c952fa0a": "len", "a91dc4bec838278871d09bbe4a7206242b852404596d71f8e7962df872da447e_left_env": "1", "9242b5b6b2f9c8d952001db1fbcffc4b477638bf957f6a5dd3a2cc742919c977": "reverse", "858e89b0997add55f954ceda806d5a77e9914f6d31bed3dedd1e9b67581ee202_left_env": "1", "6eb64b0043f60821f8c78916633c5d028164853a0ef677c0b35666673f4163ba_arguments": "((pre_$_275 post_$_276 agg_$_277) myatom newrest)", "5f7a25198045f0621cc78620e8739d731e1761a379e94ee1dae0717e4192e860": "letbinding_$_301", "c6e0bf4deaac4dc5ac02244c56fcaba2652b33bfb38f812ba0a6ef67d3049f61": "permutations", "f8de27975a5aa0f0169626c8874de2ee78c9879c44424bfc1153bc150c6282fe": "range", "5f7a25198045f0621cc78620e8739d731e1761a379e94ee1dae0717e4192e860_arguments": "(((myless_$_257 mylist_$_258) (a b)) sa sb)", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708_arguments": "((myatom) x)", "7381ec0d3e15950009d8b999a34b828e9964b79470270e7ed6c5409cf2f69bb9_arguments": "(L)", "bde59c348f4163416518c666a1c5a1d39560d2b09c9b2b38b047e4da574c745e_arguments": "(mylist)", "2e3d155705dec0e05e5d4c87c93395ffe98fffe9f21542faca3c5c8751a18047_left_env": "1", "61f9f86f9df2ce0986b2ae605b74b6111f767e89b83729af6f34110bf65bdeb2_left_env": "1", "2c82355dea6595dde07e0d5e05aa96b89052c8b97d0e0c868356df4914f2fe68": "lambda_$_307", "9fbce34b16a7c4618d5816009b42c6e661cb7efbcaf90314a2f840362f175793_left_env": "1", "483fcccf52dd01c7be19ebb1f6ec50a0c235873236a8ab545236771af634b706_arguments": "(() listlen)", "9acbd39570d133bd1318e96254949ed3f9d84fb6796324d393b68b898c4999c8": "map-with-rest", "1443073ed355cb83607d1c2a6d5612f28a038d3887fa750de973e6aa0ed83f08": "merge", "e23db45dac9015daa0e667d0d846c354b2b9d9aaa3faedb560854d8662f2e238": "letbinding_$_299", "bde59c348f4163416518c666a1c5a1d39560d2b09c9b2b38b047e4da574c745e_left_env": "1", "a0c582b1c6b05c8a3f5d5d7afd51404b874bb63f21ef73f31c383007fce81e87_arguments": "(a b)", "0871deba30de6d95a0b04a45e6e036162f8abe844ae21b31f7d7cd518845fe9c_arguments": "(next final)", "__chia__main_arguments": "(M N)", "bde59c348f4163416518c666a1c5a1d39560d2b09c9b2b38b047e4da574c745e": "split", "02c081d44a600583556699febd4e3bc2eb117b61bd5cfc612c2bf091c952fa0a_arguments": "(L)", "f8de27975a5aa0f0169626c8874de2ee78c9879c44424bfc1153bc150c6282fe_arguments": "(i)", "0871deba30de6d95a0b04a45e6e036162f8abe844ae21b31f7d7cd518845fe9c_left_env": "1", "78956ca35a10cbc40b04bd0a14ca12faae5f292d70b9e072da61ef82f3677522_left_env": "1", "e23db45dac9015daa0e667d0d846c354b2b9d9aaa3faedb560854d8662f2e238_left_env": "1", "c6e0bf4deaac4dc5ac02244c56fcaba2652b33bfb38f812ba0a6ef67d3049f61_left_env": "1", "6eb64b0043f60821f8c78916633c5d028164853a0ef677c0b35666673f4163ba": "letbinding_$_303", "1443073ed355cb83607d1c2a6d5612f28a038d3887fa750de973e6aa0ed83f08_left_env": "1", "61f9f86f9df2ce0986b2ae605b74b6111f767e89b83729af6f34110bf65bdeb2_arguments": "(x)", "a0c582b1c6b05c8a3f5d5d7afd51404b874bb63f21ef73f31c383007fce81e87_left_env": "1", "7381ec0d3e15950009d8b999a34b828e9964b79470270e7ed6c5409cf2f69bb9": "enquote-rest", "ae16459987997a1c9872031a855e1dfe8138fde4d1d5b9a5f188d3292ea97565_left_env": "1", "9fbce34b16a7c4618d5816009b42c6e661cb7efbcaf90314a2f840362f175793_arguments": "(() a b)", "858e89b0997add55f954ceda806d5a77e9914f6d31bed3dedd1e9b67581ee202": "split_inner", "78956ca35a10cbc40b04bd0a14ca12faae5f292d70b9e072da61ef82f3677522_arguments": "((first . remainder))", "f8de27975a5aa0f0169626c8874de2ee78c9879c44424bfc1153bc150c6282fe_left_env": "1", "9242b5b6b2f9c8d952001db1fbcffc4b477638bf957f6a5dd3a2cc742919c977_left_env": "1", "19f60e08ef187986cf06532ba696b5b91efeb5c043318b9e6b731f1cd80adf79_arguments": "((next . remainder))", "2c82355dea6595dde07e0d5e05aa96b89052c8b97d0e0c868356df4914f2fe68_arguments": "(() L)", "483fcccf52dd01c7be19ebb1f6ec50a0c235873236a8ab545236771af634b706_left_env": "1", "5f7a25198045f0621cc78620e8739d731e1761a379e94ee1dae0717e4192e860_left_env": "1", "5087819cc352ab86b0b64eebf0a29725fb6306291a42ac51f17253919f7b899c_arguments": "((myless_$_257 mylist_$_258) (a b))", "a91dc4bec838278871d09bbe4a7206242b852404596d71f8e7962df872da447e": "all-in-list", "42e1f82cf8542a5f31de7edcd596df9a08e48bcf0aa637f866be044a0c96841f": "merge_inner", "1c9d01e44e9c5f35a282358422033e4e960609d9f5ef0efc7c7b48bf311208ff": "letbinding_$_302", "2e3d155705dec0e05e5d4c87c93395ffe98fffe9f21542faca3c5c8751a18047_arguments": "(a b)", "19f60e08ef187986cf06532ba696b5b91efeb5c043318b9e6b731f1cd80adf79_left_env": "1", "0ebdfa3c097ba5b3ef741e8a33b6af2f741209b3e29d94c1c55deb599e4c41d7_arguments": "(myfunc mylist returnval)", "1c9d01e44e9c5f35a282358422033e4e960609d9f5ef0efc7c7b48bf311208ff_arguments": "((a_$_261 b_$_262) inner_result)", "c6e0bf4deaac4dc5ac02244c56fcaba2652b33bfb38f812ba0a6ef67d3049f61_arguments": "(vals)"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/test_prepend.clsp b/resources/tests/game-referee-in-cl21/test_prepend.clsp new file mode 100644 index 000000000..9f0d45819 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_prepend.clsp @@ -0,0 +1,5 @@ +(mod (X Y) + (include *standard-cl-21*) + (include prepend.clinc) + (prepend X Y) + ) diff --git a/resources/tests/game-referee-in-cl21/test_prepend.clvm.hex.reference b/resources/tests/game-referee-in-cl21/test_prepend.clvm.hex.reference new file mode 100644 index 000000000..47dd7f1f1 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_prepend.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff02ffff04ff02ffff04ff05ffff04ff0bff8080808080ffff04ffff01ff02ffff03ff05ffff01ff02ffff01ff04ffff05ff0580ffff02ff02ffff04ff02ffff04ffff06ff0580ffff04ff0bff808080808080ff0180ffff01ff02ffff010bff018080ff0180ff018080 diff --git a/resources/tests/game-referee-in-cl21/test_prepend.sym b/resources/tests/game-referee-in-cl21/test_prepend.sym new file mode 100644 index 000000000..368cc453d --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_prepend.sym @@ -0,0 +1 @@ +{"source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/test_prepend.clsp", "dfe61be8d5db02605605573b4d298395c7a871cc5390a79d535d07bbd2338987_left_env": "1", "__chia__main_arguments": "(X Y)", "dfe61be8d5db02605605573b4d298395c7a871cc5390a79d535d07bbd2338987": "prepend", "dfe61be8d5db02605605573b4d298395c7a871cc5390a79d535d07bbd2338987_arguments": "(a b)"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/test_range.clsp b/resources/tests/game-referee-in-cl21/test_range.clsp new file mode 100644 index 000000000..a7dc5edd9 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_range.clsp @@ -0,0 +1,6 @@ +(mod (X) + (include *standard-cl-21*) + (include range.clinc) + + (range X) + ) diff --git a/resources/tests/game-referee-in-cl21/test_range.clvm.hex.reference b/resources/tests/game-referee-in-cl21/test_range.clvm.hex.reference new file mode 100644 index 000000000..628a68fe6 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_range.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff06ffff04ff02ffff04ff05ff80808080ffff04ffff01ffff02ffff03ffff09ff05ff0b80ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff04ff05ffff02ff04ffff04ff02ffff04ffff10ff05ffff010180ffff04ff0bff808080808080ff018080ff0180ff02ff04ffff04ff02ffff04ff80ffff04ff05ff8080808080ff018080 diff --git a/resources/tests/game-referee-in-cl21/test_range.sym b/resources/tests/game-referee-in-cl21/test_range.sym new file mode 100644 index 000000000..fabe9f400 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_range.sym @@ -0,0 +1 @@ +{"0f035685b03df70de81cbdbd4e600f5d2f148f6c02b0e0a73ef96a26e7121f2a_left_env": "1", "6ba774998680757c6e60d6d6e8a94176f76b74e5f8c6f2cf5d99fb134e916556": "range_inner", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/test_range.clsp", "0f035685b03df70de81cbdbd4e600f5d2f148f6c02b0e0a73ef96a26e7121f2a_arguments": "(i)", "6ba774998680757c6e60d6d6e8a94176f76b74e5f8c6f2cf5d99fb134e916556_left_env": "1", "6ba774998680757c6e60d6d6e8a94176f76b74e5f8c6f2cf5d99fb134e916556_arguments": "(next final)", "0f035685b03df70de81cbdbd4e600f5d2f148f6c02b0e0a73ef96a26e7121f2a": "range", "__chia__main_arguments": "(X)"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/test_reverse.clsp b/resources/tests/game-referee-in-cl21/test_reverse.clsp new file mode 100644 index 000000000..b1d843648 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_reverse.clsp @@ -0,0 +1,6 @@ +(mod (X) + (include *standard-cl-21*) + (include reverse.clinc) + + (reverse X) + ) diff --git a/resources/tests/game-referee-in-cl21/test_reverse.clvm.hex.reference b/resources/tests/game-referee-in-cl21/test_reverse.clvm.hex.reference new file mode 100644 index 000000000..492966787 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_reverse.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff06ffff04ff02ffff04ff05ff80808080ffff04ffff01ffff02ffff03ff0bffff01ff02ffff01ff02ff04ffff04ff02ffff04ffff04ffff05ff0b80ff0580ffff04ffff06ff0b80ff8080808080ff0180ffff01ff02ffff0105ff018080ff0180ff02ff04ffff04ff02ffff04ff80ffff04ff05ff8080808080ff018080 diff --git a/resources/tests/game-referee-in-cl21/test_reverse.sym b/resources/tests/game-referee-in-cl21/test_reverse.sym new file mode 100644 index 000000000..e573ba66a --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_reverse.sym @@ -0,0 +1 @@ +{"0f035685b03df70de81cbdbd4e600f5d2f148f6c02b0e0a73ef96a26e7121f2a_arguments": "(vals)", "893ed9a33d9ef594068ddc5aa4e669dce55ad0e7588062968c1d76ba4c830c01_left_env": "1", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/test_reverse.clsp", "0f035685b03df70de81cbdbd4e600f5d2f148f6c02b0e0a73ef96a26e7121f2a_left_env": "1", "893ed9a33d9ef594068ddc5aa4e669dce55ad0e7588062968c1d76ba4c830c01": "reverse_inner", "0f035685b03df70de81cbdbd4e600f5d2f148f6c02b0e0a73ef96a26e7121f2a": "reverse", "893ed9a33d9ef594068ddc5aa4e669dce55ad0e7588062968c1d76ba4c830c01_arguments": "(reversed rest)", "__chia__main_arguments": "(X)"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/test_sort.clsp b/resources/tests/game-referee-in-cl21/test_sort.clsp new file mode 100644 index 000000000..8185024d0 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_sort.clsp @@ -0,0 +1,37 @@ + +(mod () + (include *standard-cl-21*) + (include print.clinc) + (include sort.clinc) + (include assert.clinc) + (include deep_compare.clinc) + (include reverse.clinc) + (include prepend.clinc) + (include map.clinc) + (include range.clinc) + (include permutations.clinc) + (include last.clinc) + (include busy.clinc) + + (defun try_list (mylist newlist) + (assert (deep= (print "sorted" (sort (lambda (A B) (deep< A B)) newlist)) mylist) 0) + ) + + (defun try_permuted_list (mylist) + (busy (lambda ((& mylist) newlist) (try_list mylist newlist)) + (print "sort all these" (permutations (print "mylist" mylist))) + 0 + ) + ) + (last + (try_list 0 0) + (try_list (range 15) (range 15)) + (try_list (range 15) (reverse (range 15))) + (try_permuted_list (list -1 -1 0 0 2)) + (busy (lambda (i) (try_permuted_list (print "sortme" (range i)))) + (range 4) + 0 + ) + 1 + ) +) diff --git a/resources/tests/game-referee-in-cl21/test_sort.clvm.hex.reference b/resources/tests/game-referee-in-cl21/test_sort.clvm.hex.reference new file mode 100644 index 000000000..4cf09e661 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_sort.clvm.hex.reference @@ -0,0 +1 @@ +ff02ffff01ff02ff66ffff04ff02ffff04ffff04ffff02ff56ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff7e80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff04ffff0101ff80808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff018080ffff04ffff0101ffff0180808080ffff0180808080ffff04ffff02ff4affff04ff02ffff04ffff0104ff80808080ffff04ffff0180ff808080808080ffff04ffff02ff6effff04ff02ffff04ffff04ffff0181ffffff04ffff0181ffffff04ffff0180ffff04ffff0180ffff04ffff0102ffff01808080808080ff80808080ffff04ffff02ff76ffff04ff02ffff04ffff02ff4affff04ff02ffff04ffff010fff80808080ffff04ffff02ff7cffff04ff02ffff04ffff02ff4affff04ff02ffff04ffff010fff80808080ff80808080ff8080808080ffff04ffff02ff76ffff04ff02ffff04ffff02ff4affff04ff02ffff04ffff010fff80808080ffff04ffff02ff4affff04ff02ffff04ffff010fff80808080ff8080808080ffff04ffff02ff76ffff04ff02ffff04ffff0180ffff04ffff0180ff8080808080ffff04ffff0101ffff0180808080808080ff80808080ffff04ffff01ffffffffff02ffff03ffff22ffff0187247072696e7424ff05ff0b80ffff01ff02ffff010bff0180ffff01ff02ffff010bff018080ff0180ffff02ffff03ff05ffff01ff02ffff01ff02ff50ffff04ff02ffff04ffff06ff0580ffff04ff17ffff04ffff04ffff05ff0580ff0b80ff808080808080ff0180ffff01ff02ffff01ff06ff0380ff018080ff0180ff02ff50ffff04ff02ffff04ff05ffff01ff80ff8080808080ffffff02ffff03ffff20ff0b80ffff01ff02ffff01ff02ff22ffff04ff02ffff04ffff02ff7cffff04ff02ffff04ff2fff80808080ffff04ff17ff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff20ff1780ffff01ff02ffff01ff02ff22ffff04ff02ffff04ffff02ff7cffff04ff02ffff04ff2fff80808080ffff04ff0bff8080808080ff0180ffff01ff02ffff01ff02ffff03ffff02ff05ffff04ffff05ff0b80ffff04ffff05ff1780ffff0180808080ffff01ff02ffff01ff02ff48ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ffff04ffff04ffff05ff0b80ff2f80ff80808080808080ff0180ffff01ff02ffff01ff02ff48ffff04ff02ffff04ff05ffff04ff0bffff04ffff06ff1780ffff04ffff04ffff05ff1780ff2f80ff80808080808080ff018080ff0180ff018080ff0180ff018080ff0180ff02ff48ffff04ff02ffff04ff05ffff04ff0bffff04ff17ffff01ff80808080808080ffff02ffff03ff0bffff01ff02ffff01ff02ffff03ffff06ff0b80ffff01ff02ffff01ff02ff78ffff04ff02ffff04ffff06ff0180ffff04ffff02ff70ffff04ff02ffff04ff0bff80808080ff8080808080ff0180ffff01ff02ffff010bff018080ff0180ff0180ffff01ff02ffff01ff0180ff018080ff0180ff02ff24ffff04ff02ffff04ff03ffff04ffff02ff58ffff04ff02ffff04ff09ffff04ff13ff8080808080ffff04ffff02ff58ffff04ff02ffff04ff09ffff04ff2bff8080808080ff808080808080ffffff02ff68ffff04ff02ffff04ff11ffff04ff0bffff04ff17ff808080808080ffff02ffff03ffff07ff0580ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff02ff74ffff04ff02ffff04ffff06ff0180ffff04ffff02ff54ffff04ff02ffff04ffff05ff0580ffff04ffff05ff0b80ff8080808080ff8080808080ff0180ffff01ff02ffff01ff0101ff018080ff0180ff0180ffff01ff02ffff01ff02ffff03ffff07ff0b80ffff01ff02ffff01ff0181ffff0180ffff01ff02ffff01ff02ffff03ffff15ff05ff0b80ffff01ff02ffff01ff0101ff0180ffff01ff02ffff01ff11ffff0180ffff15ff0bff058080ff018080ff0180ff018080ff0180ff018080ff0180ff02ffff03ff0bffff01ff02ffff010bff0180ffff01ff02ffff01ff02ff54ffff04ff02ffff04ffff06ff0980ffff04ffff06ff1580ff8080808080ff018080ff0180ffffff09ffff02ff54ffff04ff02ffff04ff05ffff04ff0bff8080808080ffff0181ff80ff09ffff02ff54ffff04ff02ffff04ff05ffff04ff0bff8080808080ff8080ffff02ffff03ff0bffff01ff02ffff01ff02ff5cffff04ff02ffff04ffff04ffff05ff0b80ff0580ffff04ffff06ff0b80ff8080808080ff0180ffff01ff02ffff0105ff018080ff0180ff02ff5cffff04ff02ffff04ff80ffff04ff05ff8080808080ffffffff02ffff03ff05ffff01ff02ffff01ff04ffff05ff0580ffff02ff22ffff04ff02ffff04ffff06ff0580ffff04ff0bff808080808080ff0180ffff01ff02ffff010bff018080ff0180ffff02ffff03ff0bffff01ff02ffff01ff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff02ff52ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff80808080808080ff0180ffff01ff02ffff0117ff018080ff0180ff02ffff03ffff09ff05ff0b80ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff04ff05ffff02ff72ffff04ff02ffff04ffff10ff05ffff010180ffff04ff0bff808080808080ff018080ff0180ffffff02ff72ffff04ff02ffff04ff80ffff04ff05ff8080808080ff02ffff03ffff20ff0b80ffff01ff02ffff0117ff0180ffff01ff02ffff01ff02ff5affff04ff02ffff04ffff06ff0180ffff04ffff05ff0b80ffff04ffff06ff0b80ff808080808080ff018080ff0180ffff02ff52ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff7a80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff04ff0bff808080ffff01ff01808080ff80808080ffff04ffff02ff46ffff04ff02ffff04ffff02ff22ffff04ff02ffff04ff09ffff04ff17ff8080808080ff80808080ffff04ffff02ff6affff04ff02ffff04ffff04ff0bff0980ffff04ff17ffff04ff2dff808080808080ff808080808080ff04ff09ff0b80ffffffff02ffff03ff05ffff01ff02ffff01ff02ff6affff04ff02ffff04ffff0180ffff04ff05ffff04ffff0180ff808080808080ff0180ffff01ff02ffff01ff01ff8080ff018080ff0180ff02ffff03ff0dffff01ff02ffff01ff02ff66ffff04ff02ffff04ff0dff80808080ff0180ffff01ff02ffff0109ff018080ff0180ffff02ffff03ff0bffff01ff02ffff01ff02ff66ffff04ff02ffff04ffff04ffff02ff05ffff04ffff05ff0b80ffff01808080ffff04ffff02ff56ffff04ff02ffff04ff05ffff04ffff06ff0b80ffff04ff17ff808080808080ffff01808080ff80808080ff0180ffff01ff02ffff0117ff018080ff0180ff02ffff03ffff02ff6cffff04ff02ffff04ffff02ff20ffff04ff02ffff04ffff0186736f72746564ffff04ffff02ff58ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff4e80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff01ffff04ffff0180ff0180808080ffff04ff0bff8080808080ff8080808080ffff04ff05ff8080808080ffff01ff02ffff01ff0180ff0180ffff01ff02ffff01ff0880ff018080ff0180ffffff02ff4cffff04ff02ffff04ff0bffff04ff17ff8080808080ff02ff56ffff04ff02ffff04ffff04ffff0102ffff04ffff04ffff0101ffff04ffff0102ffff04ffff04ffff0101ff5e80ffff04ffff04ffff0104ffff04ffff04ffff0101ff0280ffff01ff01808080ff8080808080ffff04ffff04ffff0104ffff04ffff04ffff0101ffff04ff05ff808080ffff01ff01808080ff80808080ffff04ffff02ff20ffff04ff02ffff04ffff018e736f727420616c6c207468657365ffff04ffff02ff46ffff04ff02ffff04ffff02ff20ffff04ff02ffff04ffff01866d796c697374ffff04ff05ff8080808080ff80808080ff8080808080ffff01ff808080808080ffff02ff76ffff04ff02ffff04ff09ffff04ff0bff8080808080ff02ff6effff04ff02ffff04ffff02ff20ffff04ff02ffff04ffff0186736f72746d65ffff04ffff02ff4affff04ff02ffff04ff0bff80808080ff8080808080ff80808080ff018080 diff --git a/resources/tests/game-referee-in-cl21/test_sort.sym b/resources/tests/game-referee-in-cl21/test_sort.sym new file mode 100644 index 000000000..6bea76365 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/test_sort.sym @@ -0,0 +1 @@ +{"e31ef9039be21ebbff9fca105dadf444914415d83cf1f74f6e299ccc760ca58c": "map-with-rest", "bcf06844cee9589ac2df23d9cf5d9a372b71fc3725db9c7249f0db72a2c7fdc3": "reverse", "7aaf175b403f21a9b93e40356ff378a531810f127922e726506b07915393384f": "reverse_inner", "997e0518ff66cefb408f6f6b874f0395ae1399f73650dc57f45d6f4eb79daccc_left_env": "1", "ca9b60a19af98d1290d1161582331f43d6518cbe383dcb87452e253773cf9329_arguments": "(@ everything (rest aggl aggr))", "d2586583545bb886183ab7b08b5c6561b6fdd931d8ee8e12b993d114579e6219_left_env": "1", "1d6bc99de7f858798725d96803a52ef1fa5cf7315c337ea51e560209f7366de5_arguments": "((next . remainder))", "1a3f28c2eeaf6c592d86f46dc96c10910a05739bbeda09e147e67fa64927c6f5_left_env": "1", "aff6f9e4b1d082994645068345db6e378e1839b24b4947111b4fd5d77bef5b4b": "range_inner", "2d2168734160822b5569309f8541a5b632b035f41c89907e79cb1983fc851a7b_arguments": "((myless_$_144 mylist_$_145) (a b))", "e31ef9039be21ebbff9fca105dadf444914415d83cf1f74f6e299ccc760ca58c_arguments": "(F L R)", "131cca40e81a7248781e5edf321f83b96282a9cf4e5ff0151b8dd2fdd5078499": "try_permuted_list", "2a7aff98de69b1fef1b6bc350cb81e2dd99d40254e7f541ef22f8837937ffdbb_left_env": "1", "131cca40e81a7248781e5edf321f83b96282a9cf4e5ff0151b8dd2fdd5078499_arguments": "(mylist)", "d2b16ed82a656d685d88e5e248fe3221b20f9082377d1263d95076bda402dd8d_left_env": "1", "1a3f28c2eeaf6c592d86f46dc96c10910a05739bbeda09e147e67fa64927c6f5": "sort", "b8b601fdb8fab0e12e98d10b1e8169d4209f502a901b4084e2f2c099065460e7_arguments": "(a b)", "02eae9c80ab3ae5e70fcb788e6753ac557297c5b882f6318db705b18b766e872": "letbinding_$_197", "85734bcc0a0243b28bb41bedb69f548a97a473bd3e87b1b5c8acb3a1140ba3d5_left_env": "1", "d155f473544dcda36e0bfa2a3f1579d2e32e251b1b2683a1a1eb73f919eefef2": "letbinding_$_196", "042c55b626937b8d6be258a97f8d25cdedeee2e712dbddef9438fa900eff883e_arguments": "(R P)", "d155f473544dcda36e0bfa2a3f1579d2e32e251b1b2683a1a1eb73f919eefef2_arguments": "(((myless_$_144 mylist_$_145) (a b)) sa sb)", "d5c36bb1c58c5974069b15c197ca27352476f19db460fb8cfa79fc1f35f55b13_left_env": "1", "c5bd34dcdeed87a6c78ea6b36052df8f96895a13a69b4179c15faf9f83ba4560": "lambda_$_201", "d2b16ed82a656d685d88e5e248fe3221b20f9082377d1263d95076bda402dd8d": "merge_inner", "d76bbcd6d3800803e66cb762aa942d5836553c18a50638e3dfcf3deca86f4141": "split", "24c1a323b918bc0b01064cb6171c20899e9d4e4e08a81d7e88013815e7c3fc22": "merge", "6b79b32d680005d302468e9d2c1592335cee3e4e0a454c9ede7653eb17b9c26d_arguments": "(a b)", "7aaf175b403f21a9b93e40356ff378a531810f127922e726506b07915393384f_arguments": "(reversed rest)", "bcf06844cee9589ac2df23d9cf5d9a372b71fc3725db9c7249f0db72a2c7fdc3_left_env": "1", "d76bbcd6d3800803e66cb762aa942d5836553c18a50638e3dfcf3deca86f4141_arguments": "(mylist)", "c5bd34dcdeed87a6c78ea6b36052df8f96895a13a69b4179c15faf9f83ba4560_arguments": "((mylist_$_193) newlist)", "962986191da6874f39aeb123a2daa2d87bd1d8a68f612e83d33070a0315fa3d7": "lambda_$_194", "27306e11834170f40679ff25b0e24deeb74c7a9b32495134103d6f952600b48d_arguments": "(a b)", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708": "lambda_$_199", "ca9b60a19af98d1290d1161582331f43d6518cbe383dcb87452e253773cf9329_left_env": "1", "bcf06844cee9589ac2df23d9cf5d9a372b71fc3725db9c7249f0db72a2c7fdc3_arguments": "(vals)", "e31ef9039be21ebbff9fca105dadf444914415d83cf1f74f6e299ccc760ca58c_left_env": "1", "aff6f9e4b1d082994645068345db6e378e1839b24b4947111b4fd5d77bef5b4b_left_env": "1", "aff6f9e4b1d082994645068345db6e378e1839b24b4947111b4fd5d77bef5b4b_arguments": "(next final)", "27306e11834170f40679ff25b0e24deeb74c7a9b32495134103d6f952600b48d": "deep_compare", "27306e11834170f40679ff25b0e24deeb74c7a9b32495134103d6f952600b48d_left_env": "1", "d2586583545bb886183ab7b08b5c6561b6fdd931d8ee8e12b993d114579e6219_arguments": "(mylist newlist)", "c5bd34dcdeed87a6c78ea6b36052df8f96895a13a69b4179c15faf9f83ba4560_left_env": "1", "1d6bc99de7f858798725d96803a52ef1fa5cf7315c337ea51e560209f7366de5_left_env": "1", "131cca40e81a7248781e5edf321f83b96282a9cf4e5ff0151b8dd2fdd5078499_left_env": "1", "6b79b32d680005d302468e9d2c1592335cee3e4e0a454c9ede7653eb17b9c26d_left_env": "1", "2a7aff98de69b1fef1b6bc350cb81e2dd99d40254e7f541ef22f8837937ffdbb": "busy", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708_arguments": "((myatom) x)", "0912f05321a428d040bc76941cf4f8f7c74f28662f48098303e30cf1a6a43708_left_env": "1", "5b656ea16b982e0b3da35bd883c9ff6407dadbbf05faeca2d53f4348dad67679": "prepend", "02eae9c80ab3ae5e70fcb788e6753ac557297c5b882f6318db705b18b766e872_left_env": "1", "962986191da6874f39aeb123a2daa2d87bd1d8a68f612e83d33070a0315fa3d7_arguments": "(() i)", "d155f473544dcda36e0bfa2a3f1579d2e32e251b1b2683a1a1eb73f919eefef2_left_env": "1", "042c55b626937b8d6be258a97f8d25cdedeee2e712dbddef9438fa900eff883e": "print", "997e0518ff66cefb408f6f6b874f0395ae1399f73650dc57f45d6f4eb79daccc_arguments": "(pre post agg)", "7aaf175b403f21a9b93e40356ff378a531810f127922e726506b07915393384f_left_env": "1", "d5c36bb1c58c5974069b15c197ca27352476f19db460fb8cfa79fc1f35f55b13": "letbinding_$_198", "2d2168734160822b5569309f8541a5b632b035f41c89907e79cb1983fc851a7b_left_env": "1", "ca9b60a19af98d1290d1161582331f43d6518cbe383dcb87452e253773cf9329": "split_inner", "6a477b54f8811bb3d5111ee9d39d29db7e3cca8f2521efd80f46dcfd9bc0573b_arguments": "(i)", "d76bbcd6d3800803e66cb762aa942d5836553c18a50638e3dfcf3deca86f4141_left_env": "1", "e9570c55a298e52c75e1ab676bdb601ab3a9939994f3f50be22ce4ebe6a1cc24": "permutations", "d2586583545bb886183ab7b08b5c6561b6fdd931d8ee8e12b993d114579e6219": "try_list", "85734bcc0a0243b28bb41bedb69f548a97a473bd3e87b1b5c8acb3a1140ba3d5_arguments": "(() A B)", "962986191da6874f39aeb123a2daa2d87bd1d8a68f612e83d33070a0315fa3d7_left_env": "1", "997e0518ff66cefb408f6f6b874f0395ae1399f73650dc57f45d6f4eb79daccc": "permutations_inner", "1d6bc99de7f858798725d96803a52ef1fa5cf7315c337ea51e560209f7366de5": "last_inner", "24c1a323b918bc0b01064cb6171c20899e9d4e4e08a81d7e88013815e7c3fc22_left_env": "1", "d5c36bb1c58c5974069b15c197ca27352476f19db460fb8cfa79fc1f35f55b13_arguments": "((pre_$_176 post_$_177 agg_$_178) myatom newrest)", "6a477b54f8811bb3d5111ee9d39d29db7e3cca8f2521efd80f46dcfd9bc0573b_left_env": "1", "b8b601fdb8fab0e12e98d10b1e8169d4209f502a901b4084e2f2c099065460e7_left_env": "1", "1a3f28c2eeaf6c592d86f46dc96c10910a05739bbeda09e147e67fa64927c6f5_arguments": "(myless mylist)", "__chia__main_arguments": "()", "e9570c55a298e52c75e1ab676bdb601ab3a9939994f3f50be22ce4ebe6a1cc24_arguments": "(vals)", "d2b16ed82a656d685d88e5e248fe3221b20f9082377d1263d95076bda402dd8d_arguments": "(myless A B agg)", "02eae9c80ab3ae5e70fcb788e6753ac557297c5b882f6318db705b18b766e872_arguments": "((a_$_148 b_$_149) inner_result)", "b8b601fdb8fab0e12e98d10b1e8169d4209f502a901b4084e2f2c099065460e7": "deep=", "24c1a323b918bc0b01064cb6171c20899e9d4e4e08a81d7e88013815e7c3fc22_arguments": "(myless a b)", "5b656ea16b982e0b3da35bd883c9ff6407dadbbf05faeca2d53f4348dad67679_arguments": "(a b)", "6b79b32d680005d302468e9d2c1592335cee3e4e0a454c9ede7653eb17b9c26d": "deep<", "5b656ea16b982e0b3da35bd883c9ff6407dadbbf05faeca2d53f4348dad67679_left_env": "1", "042c55b626937b8d6be258a97f8d25cdedeee2e712dbddef9438fa900eff883e_left_env": "1", "source_file": "/home/arty/dev/chia/clvm_tools_rs/resources/tests/game-referee-in-cl21/test_sort.clsp", "85734bcc0a0243b28bb41bedb69f548a97a473bd3e87b1b5c8acb3a1140ba3d5": "lambda_$_200", "2d2168734160822b5569309f8541a5b632b035f41c89907e79cb1983fc851a7b": "letbinding_$_195", "2a7aff98de69b1fef1b6bc350cb81e2dd99d40254e7f541ef22f8837937ffdbb_arguments": "(myfunc mylist returnval)", "6a477b54f8811bb3d5111ee9d39d29db7e3cca8f2521efd80f46dcfd9bc0573b": "range", "e9570c55a298e52c75e1ab676bdb601ab3a9939994f3f50be22ce4ebe6a1cc24_left_env": "1"} \ No newline at end of file diff --git a/resources/tests/game-referee-in-cl21/testnoncegame.py b/resources/tests/game-referee-in-cl21/testnoncegame.py new file mode 100644 index 000000000..3b4cd2c2a --- /dev/null +++ b/resources/tests/game-referee-in-cl21/testnoncegame.py @@ -0,0 +1,33 @@ +import hashlib + +from hsms.streamables.program import Program + +from clvm.EvalError import EvalError + +noncegame = Program.from_bytes(bytes.fromhex(open("noncegame.clvm.hex").read())) +noncehash = noncegame.tree_hash() + +def drun(prog: Program, args: Program): + try: + return prog.run(args) + except EvalError as ee: + print(f"brun -x -y main.sym {prog} {Program.to(args)}") + raise + +def testnonce(startnonce, maxnonce): + for i in range(startnonce, maxnonce): + mygame = noncegame.curry(i, noncehash) + good_parameters = [i*2, noncegame.curry(i+1, noncehash).tree_hash(), 1, (i*4, b'g')] + bad_parameters = [i*3, noncegame.curry(i+2, noncehash).tree_hash(), 2, (i*5, b'g')] + assert drun(mygame, good_parameters) == b'g' + for j in range(len(good_parameters)): + try: + p = list(good_parameters) + p[j] = bad_parameters[j] + mygame.run(p) + assert False + except EvalError as ee: + pass + +if __name__ == '__main__': + testnonce(3, 7) diff --git a/resources/tests/game-referee-in-cl21/testreferee.py b/resources/tests/game-referee-in-cl21/testreferee.py new file mode 100644 index 000000000..01eb53643 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/testreferee.py @@ -0,0 +1,479 @@ +import pytest +from hashlib import sha256 +from contextlib import asynccontextmanager +from chia.clvm.spend_sim import SimClient, SpendSim +from pathlib import Path +from clvm.casts import int_to_bytes, int_from_bytes + +from hsms.streamables.program import Program +from clvm_tools_rs import compile_clvm +from clvm_tools.binutils import disassemble + +from clvm.EvalError import EvalError +from chia.types.mempool_inclusion_status import MempoolInclusionStatus +from chia.util.errors import Err +from dataclasses import dataclass +from typing import Any +from chia_rs import Coin +from chia.types.spend_bundle import SpendBundle +from chia.types.coin_spend import CoinSpend +from blspy import G2Element + +from steprun import diag_run_clvm, compile_module_with_symbols + +compile_module_with_symbols(['.'],'referee.clsp') +referee = Program.from_bytes(bytes.fromhex(open("referee.clvm.hex").read())) +refhash = referee.tree_hash() +compile_module_with_symbols(['.'],'referee_accuse.clsp') +referee_accuse = Program.from_bytes(bytes.fromhex(open("referee_accuse.clvm.hex").read())) +refaccusehash = referee.tree_hash() +compile_clvm('rockpaperscissorsa.clsp', 'rockpaperscissorsa.clvm.hex', ['.']) +MOD_A = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsa.clvm.hex").read())) +compile_clvm('rockpaperscissorsb.clsp', 'rockpaperscissorsb.clvm.hex', ['.']) +MOD_B = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsb.clvm.hex").read())) +compile_clvm('rockpaperscissorsc.clsp', 'rockpaperscissorsc.clvm.hex', ['.']) +MOD_C = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsc.clvm.hex").read())) +compile_clvm('rockpaperscissorsd.clsp', 'rockpaperscissorsd.clvm.hex', ['.']) +MOD_D = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsd.clvm.hex").read())) + +move = 0 +accuse = 1 +timeout = 2 + +def drun(prog: Program, *args: Program): + try: + return prog.run(*args) + except EvalError as ee: + print(f"brun -x -y main.sym {prog} {Program.to(list(args))}") + raise + +def sha(blob:bytes) -> bytes: + return sha256(blob).digest() + +@pytest.fixture(scope="function") +@asynccontextmanager +async def setup_sim() : + sim = await SpendSim.create(db_path=Path("file:db_test?mode=memory&cache=shared")) + sim_client = SimClient(sim) + await sim.farm_block() + + try: + yield sim, sim_client + finally: + await sim.close() + +def bootstrap_referee(parent_coin_id, initial_validation_program_hash, initial_split, + amount, timeout, max_move_size, mover_puzzle, waiter_puzzle): + """ + returns referee_wrap + """ + puzzle_hash = referee.curry( + [initial_validation_program_hash, 0, initial_split, amount, timeout, max_move_size, mover_puzzle.tree_hash(), + waiter_puzzle.tree_hash(), referee.tree_hash()]).tree_hash() + coin = Coin(parent_coin_id, puzzle_hash, amount) + return RefereeWrap(coin, bytes(32), bytes(32), bytes(32), + initial_validation_program_hash, 0, initial_split, timeout, max_move_size, + mover_puzzle, waiter_puzzle) + +@dataclass +class RefereeWrap: + coin: Any + grandparent_id: Any + parent_validation_program_hash: Any + parent_everything_else_hash: Any + validation_program_hash: Any + move: Any + split: Any + timeout: Any + max_move_size: Any + mover_puzzle: Any + waiter_puzzle: Any + + def curried_parameters_for_our_puzzle(self, purpose, for_self, move_to_make, split, validation_program_hash): + result = Program.to([ + validation_program_hash, + move_to_make, + split, + self.coin.amount, + self.timeout, + self.max_move_size, + self.mover_puzzle.tree_hash() if for_self else self.waiter_puzzle.tree_hash(), + self.waiter_puzzle.tree_hash() if for_self else self.mover_puzzle.tree_hash(), + refhash + ]) + print(f'for {purpose} curried_parameters_for_our_puzzle is {result}') + return result + + def get_puzzle(self): + return referee.curry(self.curried_parameters_for_our_puzzle( + "GET_PUZZLE", + True, + self.move, + self.split, + self.validation_program_hash + )) + + def SpendMove(self, password, move_to_make, split, validation_program_hash): + """ + returns (solution, new RefereeWrap) + """ + print(f"MOVE referee mover_puzzle {self.mover_puzzle.tree_hash()}") + print(f"MOVE referee waiter_puzzle {self.waiter_puzzle.tree_hash()}") + curried_parameters = self.curried_parameters_for_our_puzzle( + "SPEND_MOVE", + False, + move_to_make, + split, + validation_program_hash + ) + print(f"MOVE referee curried parameters {curried_parameters}") + new_puzzle_hash = referee.curry(curried_parameters).tree_hash() + print(f"MOVE new puzzle hash {Program.to(new_puzzle_hash)}") + solution = Program.to([move, move_to_make, split, validation_program_hash, self.mover_puzzle, + [password, [51, new_puzzle_hash, self.coin.amount]]]) + coin = Coin(self.coin.name(), new_puzzle_hash, self.coin.amount) + everything_else_hash = Program.to([self.move, self.split, self.coin.amount, self.timeout, + self.max_move_size, self.mover_puzzle.tree_hash(), self.waiter_puzzle.tree_hash(), + referee.tree_hash()]).tree_hash() + return (solution, RefereeWrap(coin, self.coin.parent_coin_info, self.validation_program_hash, everything_else_hash, + validation_program_hash, move_to_make, split, self.timeout, self.max_move_size, + self.waiter_puzzle, self.mover_puzzle)) + + def SpendAccuse(self, password): + """ + returns (solution, RefereeAccuse) + """ + print(f"ACCUSE starting with puzzle hash {Program.to(self.get_puzzle().tree_hash())}") + print(f"ACCUSE parent_id {Program.to(self.coin.parent_coin_info)}") + print(f"ACCUSE referee mover_puzzle {self.mover_puzzle.tree_hash()}") + print(f"ACCUSE referee waiter_puzzle {self.waiter_puzzle.tree_hash()}") + new_puzzle_hash = referee_accuse.curry([ + self.parent_validation_program_hash, + self.validation_program_hash, + self.move, + self.split, + self.coin.amount, + self.timeout, + self.waiter_puzzle.tree_hash(), + self.mover_puzzle.tree_hash() + ]).tree_hash() + solution = Program.to([accuse, self.grandparent_id, self.parent_validation_program_hash, + self.parent_everything_else_hash, self.mover_puzzle, [password, [51, new_puzzle_hash, self.coin.amount]]]) + coin = Coin(self.coin.name(), new_puzzle_hash, self.coin.amount) + return (solution, RefereeAccuseWrap(coin, self.parent_validation_program_hash, self.validation_program_hash, + self.move, self.split, self.timeout, self.waiter_puzzle.tree_hash(), + self.mover_puzzle.tree_hash())) + + def SpendTimeout(self): + """ + returns (solution, movercoinid, waitercoinid) + """ + movercoinid = Coin(self.coin.name(), self.mover_puzzle.tree_hash(), self.split).name() + waitercoinid = Coin(self.coin.name(), self.waiter_puzzle.tree_hash(), + self.coin.amount - self.split).name() + return (Program.to((timeout, 0)), movercoinid, waitercoinid) + +@dataclass +class RefereeAccuseWrap: + coin: Any + old_validation_puzzle_hash: Any + new_validation_puzzle_hash: Any + move: Any + split: Any + timeout: Any + accused_puzzle_hash: Any + accuser_puzzle_hash: Any + + def get_puzzle(self): + return referee_accuse.curry([self.old_validation_puzzle_hash, self.new_validation_puzzle_hash, + self.move, self.split, self.coin.amount, self.timeout, self.accused_puzzle_hash, + self.accuser_puzzle_hash]) + + def SpendTimeout(self): + """ + returns (solution, coinid) + """ + coin = Coin(self.coin.name(), self.accuser_puzzle_hash, self.coin.amount) + return (Program.to(0), coin.name()) + + def SpendDefend(self, validation_program_reveal, validation_program_solution): + """ + returns (solution, coinid) + """ + solution = Program.to([validation_program_reveal, validation_program_solution]) + coin = Coin(self.coin.name(), self.accused_puzzle_hash, self.coin.amount) + return (solution, coin.name()) + +@pytest.mark.asyncio +@pytest.mark.parametrize('amove', [0, 1, 2]) +@pytest.mark.parametrize('bmove', [0, 1, 2]) +async def test_rps(amove, bmove, setup_sim): + total = 100 + alice_final = (total//2 if amove == bmove else (0 if bmove == (amove + 1) % 3 else total)) + alice_preimage = int_to_bytes(60 + amove) + alice_image = sha(alice_preimage) + bob_preimage = int_to_bytes(60 + bmove) + bob_image = sha(bob_preimage) + alice_move = int_to_bytes(amove) + nil = Program.to(0) + + # (mod (password . conditions) (if (= password 'alice') conditions (x))) + alice_puzzle = Program.from_bytes(bytes.fromhex('ff02ffff03ffff09ff02ffff0185616c69636580ffff0103ffff01ff088080ff0180')) + alice_puzzle_hash = alice_puzzle.tree_hash() + # (mod (password . conditions) (if (= password 'bob') conditions (x))) + bob_puzzle = Program.from_bytes(bytes.fromhex('ff02ffff03ffff09ff02ffff0183626f6280ffff0103ffff01ff088080ff0180')) + bob_puzzle_hash = bob_puzzle.tree_hash() + + async with setup_sim as (sym, client): + acs = Program.to(1) + acs_hash = acs.tree_hash() + await sym.farm_block(acs_hash) + mycoin = (await client.get_coin_records_by_puzzle_hashes([acs_hash], include_spent_coins = False))[0].coin + # make a coin for a game + referee = bootstrap_referee(mycoin.name(), MOD_A.tree_hash(), 2, total, 1000, 50, alice_puzzle, bob_puzzle) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(mycoin, acs, Program.to([[51, referee.coin.puzzle_hash, + referee.coin.amount]]))], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + savepoint = sym.block_height + # Alice accuse Bob of cheating (negative test, should fail) + solution, accuse = referee.SpendAccuse('alice') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(referee.coin, referee.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.ASSERT_MY_PARENT_ID_FAILED + # timeout too early fail + solution, alice_reward_id, bob_reward_id = referee.SpendTimeout() + spend = SpendBundle([CoinSpend(referee.coin, referee.get_puzzle(), solution)], G2Element()) + (status, err) = await client.push_tx(spend) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.ASSERT_SECONDS_RELATIVE_FAILED + # timeout succeeds + sym.pass_time(2000) + await sym.farm_block() + (status, err) = await client.push_tx(spend) + assert status == MempoolInclusionStatus.SUCCESS + assert err is None + await sym.farm_block() + assert (await client.get_coin_records_by_names([alice_reward_id], include_spent_coins = False))[0].coin.amount == 2 + assert (await client.get_coin_records_by_names([bob_reward_id], include_spent_coins = False))[0].coin.amount == total - 2 + await sym.rewind(savepoint) + # Alice makes an illegally large move, fails + solution, ref2 = referee.SpendMove('alice', bytes(100), 0, bytes(32)) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(referee.coin, referee.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + # Alice makes move with negative split, fails + solution, ref2 = referee.SpendMove('alice', 'abc', -1, bytes(32)) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(referee.coin, referee.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + # Alice makes move with split greater than amount, fails + solution, ref2 = referee.SpendMove('alice', 'abc', referee.coin.amount + 1, bytes(32)) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(referee.coin, referee.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + # Alice move 1 commit to image + bpuz = MOD_B.curry(alice_image) + solution, ref2 = referee.SpendMove('alice', alice_image, 0, bpuz.tree_hash()) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(referee.coin, referee.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + savepoint = sym.block_height + # Bob accuse Alice of cheating + solution, accuse = ref2.SpendAccuse('bob') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref2.coin, ref2.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + savepoint2 = sym.block_height + # Alice accusation defend, gets everything + solution, reward_id = accuse.SpendDefend(MOD_A, nil) + print(solution) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + reward_coin_wrapper = await client.get_coin_records_by_names([reward_id], include_spent_coins = + False) + reward_coin = reward_coin_wrapper[0].coin + assert reward_coin.amount == referee.coin.amount + assert reward_coin.puzzle_hash == alice_puzzle_hash + await sym.rewind(savepoint2) + # accusation timeout too early fail + solution, reward_id = accuse.SpendTimeout() + spend = SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), solution)], G2Element()) + (status, err) = await client.push_tx(spend) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.ASSERT_SECONDS_RELATIVE_FAILED + # accusation timeout succeed, Bob gets everything + sym.pass_time(2000) + await sym.farm_block() + (status, err) = await client.push_tx(spend) + assert status == MempoolInclusionStatus.SUCCESS + assert err is None + await sym.farm_block() + reward_coin_wrapper = await client.get_coin_records_by_names([reward_id], include_spent_coins = + False) + reward_coin = reward_coin_wrapper[0].coin + assert reward_coin.amount == referee.coin.amount + assert reward_coin.puzzle_hash == bob_puzzle_hash + await sym.rewind(savepoint) + # Bob move 2 commit to image + cpuz = MOD_C.curry([alice_image, bob_image]) + solution, ref3 = ref2.SpendMove('bob', bob_image, 0, cpuz.tree_hash()) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref2.coin, ref2.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + savepoint = sym.block_height + # Alice accuse + solution, accuse = ref3.SpendAccuse('alice') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref3.coin, ref3.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Bob defends + solution, reward_id = accuse.SpendDefend(bpuz, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + reward_coin = (await client.get_coin_records_by_names([reward_id], include_spent_coins = + False))[0].coin + assert reward_coin.amount == referee.coin.amount + assert reward_coin.puzzle_hash == bob_puzzle_hash + await sym.rewind(savepoint) + # Alice reveals wrong preimage + alice_bad_preimage = int_to_bytes(61 + amove) + dpuz = MOD_D.curry([(amove + 1) % 3, bob_image]) + solution, ref4 = ref3.SpendMove('alice', alice_bad_preimage, 0, dpuz.tree_hash()) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref3.coin, ref3.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Bob accuses + solution, accuse = ref4.SpendAccuse('bob') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref4.coin, ref4.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Alice defends, fails + solution, reward_id = accuse.SpendDefend(cpuz, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + await sym.rewind(savepoint) + # Alice move 3 reveal preimage + dpuz = MOD_D.curry([alice_move, bob_image]) + solution, ref4 = ref3.SpendMove('alice', alice_preimage, 0, dpuz.tree_hash()) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref3.coin, ref3.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + savepoint = sym.block_height + # Bob accuses + solution, accuse = ref4.SpendAccuse('bob') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref4.coin, ref4.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Alice defends + solution, reward_id = accuse.SpendDefend(cpuz, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.rewind(savepoint) + # Bob move 4 reveal wrong preimage + bob_bad_preimage = int_to_bytes(121 + amove) + solution, ref5 = ref4.SpendMove('bob', bob_bad_preimage, 0, dpuz.tree_hash()) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref4.coin, ref4.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Alice accuses + solution, accuse = ref5.SpendAccuse('alice') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref5.coin, ref5.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Bob attempts defense, fails + solution, reward_id = accuse.SpendDefend(dpuz, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + # Bob attempts defense with wrong validation program, fails + solution, reward_id = accuse.SpendDefend(acs, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + await sym.rewind(savepoint) + if amove == bmove: + # Bob move 4 gives wrong split + solution, ref5 = ref4.SpendMove('bob', bob_preimage, 0, dpuz.tree_hash()) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref4.coin, ref4.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Alice accuses + solution, accuse = ref5.SpendAccuse('alice') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref5.coin, ref5.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Bob attempts defense, fails + solution, reward_id = accuse.SpendDefend(dpuz, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + await sym.rewind(savepoint) + # Bob move 4 reveal preimage + solution, ref5 = ref4.SpendMove('bob', bob_preimage, alice_final, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref4.coin, ref4.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + savepoint = sym.block_height + # Alice attempts move, fails + solution, ref6 = ref5.SpendMove('alice', nil, 0, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref5.coin, ref5.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.FAILED + assert err == Err.GENERATOR_RUNTIME_ERROR + # timeout, split correct + sym.pass_time(2000) + await sym.farm_block() + solution, alice_reward_id, bob_reward_id = ref5.SpendTimeout() + spend = SpendBundle([CoinSpend(ref5.coin, ref5.get_puzzle(), solution)], G2Element()) + (status, err) = await client.push_tx(spend) + assert status == MempoolInclusionStatus.SUCCESS + assert err is None + await sym.farm_block() + if alice_final != 0: + assert (await client.get_coin_records_by_names([alice_reward_id], include_spent_coins = False))[0].coin.amount == alice_final + else: + assert len(await client.get_coin_records_by_names([alice_reward_id], include_spent_coins = False)) == 0 + if alice_final != ref5.coin.amount: + assert (await client.get_coin_records_by_names([bob_reward_id], include_spent_coins = False))[0].coin.amount == ref5.coin.amount - alice_final + else: + assert len(await client.get_coin_records_by_names([bob_reward_id], include_spent_coins = False)) == 0 + await sym.rewind(savepoint) + # Alice accuses + solution, accuse = ref5.SpendAccuse('alice') + (status, err) = await client.push_tx(SpendBundle([CoinSpend(ref5.coin, ref5.get_puzzle(), + solution)], G2Element())) + assert status == MempoolInclusionStatus.SUCCESS + await sym.farm_block() + # Bob defends + solution, reward_id = accuse.SpendDefend(dpuz, nil) + (status, err) = await client.push_tx(SpendBundle([CoinSpend(accuse.coin, accuse.get_puzzle(), + solution)], G2Element())) + assert (status, err) == (MempoolInclusionStatus.SUCCESS, None) diff --git a/resources/tests/game-referee-in-cl21/testrockpaperscissors.py b/resources/tests/game-referee-in-cl21/testrockpaperscissors.py new file mode 100644 index 000000000..ed45dbccc --- /dev/null +++ b/resources/tests/game-referee-in-cl21/testrockpaperscissors.py @@ -0,0 +1,48 @@ +import hashlib + +from hsms.streamables.program import Program +from hsms.puzzles.load_clvm import load_clvm + +from clvm.EvalError import EvalError + + +MOD_A = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsa.clvm.hex").read())) +MOD_B = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsb.clvm.hex").read())) +MOD_C = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsc.clvm.hex").read())) +MOD_D = Program.from_bytes(bytes.fromhex(open("rockpaperscissorsd.clvm.hex").read())) + + +def drun(prog: Program, *args: Program): + try: + return prog.run(*args) + except EvalError as ee: + print(f"brun -x -y main.sym {prog} {Program.to(list(args))}") + raise + +def sha256(blob:bytes) -> bytes: + return hashlib.sha256(blob).digest() + +def testrps(amove, bmove): + total = 100 + alice_final = (total//2 if amove == bmove else (0 if bmove == (amove + 1) % 3 else total)) + alice_preimage = Program.to(60 + amove) + bob_preimage = Program.to(60 + bmove) + alice_image = sha256(alice_preimage.atom) + bob_image = sha256(bob_preimage.atom) + alice_move = Program.to(amove) + + cd = MOD_D.curry(alice_move, bob_image) + assert cd.run([total, bob_preimage, b'', alice_final, b'j']).atom == b'j' + cc = MOD_C.curry(alice_image, bob_image) + assert cc.run([total, alice_preimage, cd.tree_hash(), 0, b'j']).atom == b'j' + cb = MOD_B.curry(alice_image) + assert cb.run([total, bob_image, cc.tree_hash(), 0, b'j']).atom == b'j' + assert MOD_A.run([total, alice_image, cb.tree_hash(), 0, b'j']).atom == b'j' + +def testall(): + for i in range(3): + for j in range(3): + testrps(i, j) + +if __name__ == '__main__': + testall() diff --git a/resources/tests/game-referee-in-cl21/utils.clinc b/resources/tests/game-referee-in-cl21/utils.clinc new file mode 100644 index 000000000..7d754e001 --- /dev/null +++ b/resources/tests/game-referee-in-cl21/utils.clinc @@ -0,0 +1,17 @@ +( + (defmacro ifc ARGS + (defun list-length (lst) + (if (l lst) + (+ 1 (list-length (r lst))) + 0 + ) + ) + (defun do-continued-if (ARGS) + (if (= (list-length ARGS) 3) + (qq (i (unquote (f ARGS)) (com (unquote (f (r ARGS)))) (com (unquote (f (r (r ARGS))))))) + (qq (i (unquote (f ARGS)) (com (unquote (f (r ARGS)))) (unquote (do-continued-if (r (r ARGS)))))) + ) + ) + (qq (a (unquote (do-continued-if ARGS)) @)) + ) +) diff --git a/resources/tests/lib/__init__.py b/resources/tests/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/resources/tests/lib/load_clvm.py b/resources/tests/lib/load_clvm.py new file mode 100644 index 000000000..a01f37fbc --- /dev/null +++ b/resources/tests/lib/load_clvm.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import importlib +import inspect +import os +import pathlib +import sys +import tempfile +from typing import List + +import pkg_resources +from clvm_tools_rs import compile_clvm as compile_clvm_rust + +from chia.types.blockchain_format.program import Program +from chia.types.blockchain_format.serialized_program import SerializedProgram +from chia.util.lock import Lockfile + +compile_clvm_py = None + +recompile_requested = ( + (os.environ.get("CHIA_DEV_COMPILE_CLVM_ON_IMPORT", "") != "") or ("pytest" in sys.modules) +) and os.environ.get("CHIA_DEV_COMPILE_CLVM_DISABLED", None) is None + + +def translate_path(p_): + p = str(p_) + if os.path.isdir(p): + return p + else: + module_object = importlib.import_module(p) + return os.path.dirname(inspect.getfile(module_object)) + + +# Handle optional use of python clvm_tools if available and requested +if "CLVM_TOOLS" in os.environ: + try: + from clvm_tools.clvmc import compile_clvm as compile_clvm_py_candidate + + compile_clvm_py = compile_clvm_py_candidate + finally: + pass + + +def compile_clvm_in_lock(full_path: pathlib.Path, output: pathlib.Path, search_paths: List[pathlib.Path]): + # Compile using rust (default) + + # Ensure path translation is done in the idiomatic way currently + # expected. It can use either a filesystem path or name a python + # module. + treated_include_paths = list(map(translate_path, search_paths)) + res = compile_clvm_rust(str(full_path), str(output), treated_include_paths) + + if "CLVM_TOOLS" in os.environ and os.environ["CLVM_TOOLS"] == "check" and compile_clvm_py is not None: + # Simple helper to read the compiled output + def sha256file(f): + import hashlib + + m = hashlib.sha256() + m.update(open(f).read().strip().encode("utf8")) + return m.hexdigest() + + orig = "%s.orig" % output + + compile_clvm_py(full_path, orig, search_paths=search_paths) + orig256 = sha256file(orig) + rs256 = sha256file(output) + + if orig256 != rs256: + print("Compiled original %s: %s vs rust %s\n" % (full_path, orig256, rs256)) + print("Aborting compilation due to mismatch with rust") + assert orig256 == rs256 + else: + print("Compilation match %s: %s\n" % (full_path, orig256)) + + return res + + +def compile_clvm(full_path: pathlib.Path, output: pathlib.Path, search_paths: List[pathlib.Path] = []): + with Lockfile.create(pathlib.Path(tempfile.gettempdir()) / "clvm_compile" / full_path.name): + compile_clvm_in_lock(full_path, output, search_paths) + + +def load_serialized_clvm( + clvm_filename, package_or_requirement=__name__, include_standard_libraries: bool = False, recompile: bool = True +) -> SerializedProgram: + """ + This function takes a .clsp file in the given package and compiles it to a + .clsp.hex file if the .hex file is missing or older than the .clsp file, then + returns the contents of the .hex file as a `Program`. + + clvm_filename: file name + package_or_requirement: usually `__name__` if the clvm file is in the same package + """ + hex_filename = f"{clvm_filename}.hex" + + # Set the CHIA_DEV_COMPILE_CLVM_ON_IMPORT environment variable to anything except + # "" or "0" to trigger automatic recompilation of the Chialisp on load. + if recompile: + try: + if pkg_resources.resource_exists(package_or_requirement, clvm_filename): + # Establish whether the size is zero on entry + full_path = pathlib.Path(pkg_resources.resource_filename(package_or_requirement, clvm_filename)) + output = full_path.parent / hex_filename + if not output.exists() or os.stat(full_path).st_mtime > os.stat(output).st_mtime: + search_paths = [full_path.parent] + if include_standard_libraries: + # we can't get the dir, but we can get a file then get its parent. + chia_puzzles_path = pathlib.Path( + pkg_resources.resource_filename(__name__, "__init__.py") + ).parent + search_paths.append(chia_puzzles_path) + compile_clvm(full_path, output, search_paths=search_paths) + + except NotImplementedError: + # pyinstaller doesn't support `pkg_resources.resource_exists` + # so we just fall through to loading the hex clvm + pass + + clvm_hex = pkg_resources.resource_string(package_or_requirement, hex_filename).decode("utf8") + assert len(clvm_hex.strip()) != 0 + clvm_blob = bytes.fromhex(clvm_hex) + return SerializedProgram.from_bytes(clvm_blob) + + +def load_clvm( + clvm_filename, + package_or_requirement=__name__, + include_standard_libraries: bool = False, + recompile: bool = True, +) -> Program: + return Program.from_bytes( + bytes( + load_serialized_clvm( + clvm_filename, + package_or_requirement=package_or_requirement, + include_standard_libraries=include_standard_libraries, + recompile=recompile, + ) + ) + ) + + +def load_clvm_maybe_recompile( + clvm_filename, + package_or_requirement=__name__, + include_standard_libraries: bool = False, + recompile: bool = recompile_requested, +) -> Program: + return load_clvm( + clvm_filename=clvm_filename, + package_or_requirement=package_or_requirement, + include_standard_libraries=include_standard_libraries, + recompile=recompile, + ) + + +def load_serialized_clvm_maybe_recompile( + clvm_filename, + package_or_requirement=__name__, + include_standard_libraries: bool = False, + recompile: bool = recompile_requested, +) -> SerializedProgram: + return load_serialized_clvm( + clvm_filename=clvm_filename, + package_or_requirement=package_or_requirement, + include_standard_libraries=include_standard_libraries, + recompile=recompile, + ) diff --git a/resources/tests/lib/program.py b/resources/tests/lib/program.py new file mode 100644 index 000000000..eff5badd0 --- /dev/null +++ b/resources/tests/lib/program.py @@ -0,0 +1,231 @@ +from __future__ import annotations + +import io +from typing import Any, Callable, Dict, Set, Tuple + +from chia_rs import run_chia_program, tree_hash +from clvm import SExp +from clvm.casts import int_from_bytes +from clvm.EvalError import EvalError +from clvm.serialize import sexp_from_stream, sexp_to_stream + +from chia.types.blockchain_format.sized_bytes import bytes32 +from chia.util.byte_types import hexstr_to_bytes +from chia.util.hash import std_hash + +from .tree_hash import sha256_treehash + +INFINITE_COST = 11000000000 + + +class Program(SExp): + """ + A thin wrapper around s-expression data intended to be invoked with "eval". + """ + + @classmethod + def parse(cls, f) -> "Program": + return sexp_from_stream(f, cls.to) + + def stream(self, f): + sexp_to_stream(self, f) + + @classmethod + def from_bytes(cls, blob: bytes) -> Program: + # this runs the program "1", which just returns the first argument. + # the first argument is the buffer we want to parse. This effectively + # leverages the rust parser and LazyNode, making it a lot faster to + # parse serialized programs into a python compatible structure + cost, ret = run_chia_program( + b"\x01", + blob, + 50, + 0, + ) + return Program.to(ret) + + @classmethod + def fromhex(cls, hexstr: str) -> "Program": + return cls.from_bytes(hexstr_to_bytes(hexstr)) + + def __bytes__(self) -> bytes: + f = io.BytesIO() + self.stream(f) # noqa + return f.getvalue() + + def __str__(self) -> str: + return bytes(self).hex() + + def at(self, position: str) -> "Program": + """ + Take a string of only `f` and `r` characters and follow the corresponding path. + + Example: + + `assert Program.to(17) == Program.to([10, 20, 30, [15, 17], 40, 50]).at("rrrfrf")` + + """ + v = self + for c in position.lower(): + if c == "f": + v = v.first() + elif c == "r": + v = v.rest() + else: + raise ValueError(f"`at` got illegal character `{c}`. Only `f` & `r` allowed") + return v + + def replace(self, **kwargs) -> "Program": + """ + Create a new program replacing the given paths (using `at` syntax). + Example: + ``` + >>> p1 = Program.to([100, 200, 300]) + >>> print(p1.replace(f=105) == Program.to([105, 200, 300])) + True + >>> print(p1.replace(rrf=[301, 302]) == Program.to([100, 200, [301, 302]])) + True + >>> print(p1.replace(f=105, rrf=[301, 302]) == Program.to([105, 200, [301, 302]])) + True + ``` + + This is a convenience method intended for use in the wallet or command-line hacks where + it would be easier to morph elements of an existing clvm object tree than to rebuild + one from scratch. + + Note that `Program` objects are immutable. This function returns a new object; the + original is left as-is. + """ + return _sexp_replace(self, self.to, **kwargs) + + def get_tree_hash_precalc(self, *args: bytes32) -> bytes32: + """ + Any values in `args` that appear in the tree + are presumed to have been hashed already. + """ + return sha256_treehash(self, set(args)) + + def get_tree_hash(self) -> bytes32: + return bytes32(tree_hash(bytes(self))) + + def run_with_cost(self, max_cost: int, args) -> Tuple[int, "Program"]: + prog_args = Program.to(args) + cost, r = run_chia_program(self.as_bin(), prog_args.as_bin(), max_cost, 0) + return cost, Program.to(r) + + def run(self, args) -> "Program": + cost, r = self.run_with_cost(INFINITE_COST, args) + return r + + # Replicates the curry function from clvm_tools, taking advantage of *args + # being a list. We iterate through args in reverse building the code to + # create a clvm list. + # + # Given arguments to a function addressable by the '1' reference in clvm + # + # fixed_args = 1 + # + # Each arg is prepended as fixed_args = (c (q . arg) fixed_args) + # + # The resulting argument list is interpreted with apply (2) + # + # (2 (1 . self) rest) + # + # Resulting in a function which places its own arguments after those + # curried in in the form of a proper list. + def curry(self, *args) -> "Program": + fixed_args: Any = 1 + for arg in reversed(args): + fixed_args = [4, (1, arg), fixed_args] + return Program.to([2, (1, self), fixed_args]) + + def uncurry(self) -> Tuple[Program, Program]: + def match(o: SExp, expected: bytes) -> None: + if o.atom != expected: + raise ValueError(f"expected: {expected.hex()}") + + try: + # (2 (1 . ) ) + ev, quoted_inner, args_list = self.as_iter() + match(ev, b"\x02") + match(quoted_inner.pair[0], b"\x01") + mod = quoted_inner.pair[1] + args = [] + while args_list.pair is not None: + # (4 (1 . ) ) + cons, quoted_arg, rest = args_list.as_iter() + match(cons, b"\x04") + match(quoted_arg.pair[0], b"\x01") + args.append(quoted_arg.pair[1]) + args_list = rest + match(args_list, b"\x01") + return Program.to(mod), Program.to(args) + except ValueError: # too many values to unpack + # when unpacking as_iter() + # or when a match() fails + return self, self.to(0) + except TypeError: # NoneType not subscriptable + # when an object is not a pair or atom as expected + return self, self.to(0) + except EvalError: # first of non-cons + # when as_iter() fails + return self, self.to(0) + + def as_int(self) -> int: + return int_from_bytes(self.as_atom()) + + def __deepcopy__(self, memo): + return type(self).from_bytes(bytes(self)) + + EvalError = EvalError + + +def _tree_hash(node: SExp, precalculated: Set[bytes32]) -> bytes32: + """ + Hash values in `precalculated` are presumed to have been hashed already. + """ + if node.listp(): + left = _tree_hash(node.first(), precalculated) + right = _tree_hash(node.rest(), precalculated) + s = b"\2" + left + right + else: + atom = node.as_atom() + if atom in precalculated: + return bytes32(atom) + s = b"\1" + atom + return bytes32(std_hash(s)) + + +NIL = Program.from_bytes(b"\x80") + + +def _sexp_replace(sexp: SExp, to_sexp: Callable[[Any], SExp], **kwargs) -> SExp: + # if `kwargs == {}` then `return sexp` unchanged + if len(kwargs) == 0: + return sexp + + if "" in kwargs: + if len(kwargs) > 1: + raise ValueError("conflicting paths") + return kwargs[""] + + # we've confirmed that no `kwargs` is the empty string. + # Now split `kwargs` into two groups: those + # that start with `f` and those that start with `r` + + args_by_prefix: Dict[str, SExp] = {} + for k, v in kwargs.items(): + c = k[0] + if c not in "fr": + raise ValueError("bad path containing %s: must only contain `f` and `r`") + args_by_prefix.setdefault(c, dict())[k[1:]] = v + + pair = sexp.pair + if pair is None: + raise ValueError("path into atom") + + # recurse down the tree + new_f = _sexp_replace(pair[0], to_sexp, **args_by_prefix.get("f", {})) + new_r = _sexp_replace(pair[1], to_sexp, **args_by_prefix.get("r", {})) + + return to_sexp((new_f, new_r)) diff --git a/resources/tests/lib/smoke_test_deep_compare.clsp b/resources/tests/lib/smoke_test_deep_compare.clsp new file mode 100644 index 000000000..8df629518 --- /dev/null +++ b/resources/tests/lib/smoke_test_deep_compare.clsp @@ -0,0 +1,28 @@ +(mod () + (include *standard-cl-21*) + (include print.clinc) + (include prepend.clinc) + (include sort.clinc) + (include assert.clinc) + (include map.clinc) + (include deep_compare.clinc) + + (map + (lambda ((want_cmp_val cmp_a cmp_b)) + (= (deep_compare cmp_a cmp_b) want_cmp_val) + ) + (q + (0 0 0) + (-1 () (1 14 5 4 3 2)) + (1 (1 14 5 4 3 2) ()) + (-1 "X" (1 2)) + (1 (1 2) "X") + (0 (3 2) (3 2)) + (-1 (3 1) (3 3)) + (1 (3 3) (3 1)) + (-1 (1 1) (2 1)) + (1 (3 1) (2 2)) + (-1 (2 2) (3 1)) + ) + ) + ) diff --git a/resources/tests/lib/smoke_test_permutations.clsp b/resources/tests/lib/smoke_test_permutations.clsp new file mode 100644 index 000000000..2ed3fbbcc --- /dev/null +++ b/resources/tests/lib/smoke_test_permutations.clsp @@ -0,0 +1,9 @@ +(mod (X) + (include *standard-cl-21*) + (include map.clinc) + (include prepend.clinc) + (include print.clinc) + (include permutations.clinc) + + (permutations X) + ) diff --git a/resources/tests/lib/smoke_test_sort.clsp b/resources/tests/lib/smoke_test_sort.clsp new file mode 100644 index 000000000..59b8a886e --- /dev/null +++ b/resources/tests/lib/smoke_test_sort.clsp @@ -0,0 +1,11 @@ +(mod (X) + (include *standard-cl-21*) + (include prepend.clinc) + (include sort.clinc) + (include assert.clinc) + (include map.clinc) + (include reverse.clinc) + (include print.clinc) + + (sort (lambda (a b) (> b a)) X) + ) diff --git a/resources/tests/lib/steprun.py b/resources/tests/lib/steprun.py new file mode 100644 index 000000000..02db51339 --- /dev/null +++ b/resources/tests/lib/steprun.py @@ -0,0 +1,69 @@ +import binascii +import json +import os +from pathlib import Path +from typing import List + +# from chia.types.blockchain_format.program import Program +from clvm_rs import Program +from clvm_tools.binutils import assemble, disassemble + +from clvm_tools_rs import compile_clvm, compose_run_function, start_clvm_program + + +def compile_module_with_symbols(include_paths: List[Path], source: Path): + path_obj = Path(source) + file_path = path_obj.parent + file_stem = path_obj.stem + target_file = file_path / (file_stem + ".clvm.hex") + sym_file = file_path / (file_stem + ".sym") + # print(f"compile_clvm({path_obj.absolute()}, {str(target_file.absolute().as_posix())}, {include_paths}, True)") + compile_result = compile_clvm( + str(path_obj.resolve()), str(target_file.absolute()), [str(p) for p in include_paths], True + ) + print(f"Writing to {target_file} {compile_result}") + # symbols = compile_result["symbols"] + # if len(symbols) != 0: + # with open(str(sym_file.absolute()), "w") as symfile: + # symfile.write(json.dumps(symbols)) + + +def run_until_end(p): + last = None + location = None + + while not p.is_ended(): + step_result = p.step() + if step_result is not None: + last = step_result + if "Print" in last: + to_print = last["Print"] + if "Print-Location" in last: + print(f"{last['Print-Location']}: print {to_print}") + else: + print(f"print {to_print}") + + return last + + +def diag_run_clvm(program, args, symbols): + hex_form_of_program = binascii.hexlify(bytes(program)).decode("utf8") + hex_form_of_args = binascii.hexlify(bytes(args)).decode("utf8") + symbols = json.loads(open(symbols).read()) + p = start_clvm_program( + hex_form_of_program, hex_form_of_args, symbols, None + ) + report = run_until_end(p) + if "Failure" in report: + raise Exception(report) + else: + return assemble(report["Final"]) + + +if __name__ == "__main__": + # smoke test + import sys + + program = Program.fromhex(open(sys.argv[1]).read()) + args = Program.fromhex(open(sys.argv[2]).read()) + diag_run_clvm(program, args) diff --git a/resources/tests/lib/tree_hash.py b/resources/tests/lib/tree_hash.py new file mode 100644 index 000000000..1e59e9b7c --- /dev/null +++ b/resources/tests/lib/tree_hash.py @@ -0,0 +1,62 @@ +""" +This is an implementation of `sha256_treehash`, used to calculate +puzzle hashes in clvm. + +This implementation goes to great pains to be non-recursive so we don't +have to worry about blowing out the python stack. +""" + +from __future__ import annotations + +from typing import Callable, List, Optional, Set + +from clvm import CLVMObject + +from chia.types.blockchain_format.sized_bytes import bytes32 +from chia.util.hash import std_hash + +Op = Callable[[List["CLVMObject"], List["Op"], Set[bytes32]], None] + + +def sha256_treehash(sexp: CLVMObject, precalculated: Optional[Set[bytes32]] = None) -> bytes32: + """ + Hash values in `precalculated` are presumed to have been hashed already. + """ + + if precalculated is None: + precalculated = set() + + def handle_sexp(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: Set[bytes32]) -> None: + sexp = sexp_stack.pop() + if sexp.pair: + p0, p1 = sexp.pair + sexp_stack.append(p0) + sexp_stack.append(p1) + op_stack.append(handle_pair) + op_stack.append(handle_sexp) + op_stack.append(roll) + op_stack.append(handle_sexp) + else: + if sexp.atom in precalculated: + r = sexp.atom + else: + r = std_hash(b"\1" + sexp.atom) + sexp_stack.append(r) + + def handle_pair(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: Set[bytes32]) -> None: + p0 = sexp_stack.pop() + p1 = sexp_stack.pop() + sexp_stack.append(std_hash(b"\2" + p0 + p1)) + + def roll(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: Set[bytes32]) -> None: + p0 = sexp_stack.pop() + p1 = sexp_stack.pop() + sexp_stack.append(p0) + sexp_stack.append(p1) + + sexp_stack = [sexp] + op_stack: List[Op] = [handle_sexp] + while len(op_stack) > 0: + op = op_stack.pop() + op(sexp_stack, op_stack, precalculated) + return bytes32(sexp_stack[0]) diff --git a/resources/tests/rps-referee-uncaptured.clsp b/resources/tests/rps-referee-uncaptured.clsp new file mode 100644 index 000000000..1dcf38858 --- /dev/null +++ b/resources/tests/rps-referee-uncaptured.clsp @@ -0,0 +1,29 @@ +(mod ((VALIDATION_PROGRAM_HASH AMOUNT) action . args) + + (include *standard-cl-21*) + + (defun reduce (fun lst init) + (if lst + (reduce fun (r lst) (a fun (list (f lst) init))) + init + ) + ) + + (let + ((new_puzzle_hash 38911) + ) + (reduce + (lambda ((@ condition (condname arg1 arg2)) agg) + (if agg + 1 + (if (= condname CREATE_COIN) + (logand (= arg1 new_puzzle_hash) (= arg2 AMOUNT)) + 0 + ) + ) + ) + conditions + 0 + ) + ) + ) diff --git a/resources/tests/rps-referee.clsp b/resources/tests/rps-referee.clsp new file mode 100644 index 000000000..38741b09f --- /dev/null +++ b/resources/tests/rps-referee.clsp @@ -0,0 +1,29 @@ +(mod ((VALIDATION_PROGRAM_HASH AMOUNT) action . args) + + (include *standard-cl-21*) + + (defun reduce (fun lst init) + (if lst + (reduce fun (r lst) (a fun (list (f lst) init))) + init + ) + ) + + (let + ((new_puzzle_hash 38911) + ) + (reduce + (lambda ((& AMOUNT new_puzzle_hash) (@ condition (condname arg1 arg2)) agg) + (if agg + 1 + (if (= condname CREATE_COIN) + (logand (= arg1 new_puzzle_hash) (= arg2 AMOUNT)) + 0 + ) + ) + ) + conditions + 0 + ) + ) + ) diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs index f224c6852..5021813a6 100644 --- a/src/compiler/codegen.rs +++ b/src/compiler/codegen.rs @@ -21,6 +21,7 @@ use crate::compiler::evaluate::{Evaluator, EVAL_STACK_LIMIT}; use crate::compiler::frontend::{compile_bodyform, make_provides_set}; use crate::compiler::gensym::gensym; use crate::compiler::inline::{replace_in_inline, synthesize_args}; +use crate::compiler::lambda::lambda_codegen; use crate::compiler::optimize::optimize_expr; use crate::compiler::prims::{primapply, primcons, primquote}; use crate::compiler::runtypes::RunFailure; @@ -181,18 +182,103 @@ fn create_name_lookup_( } } +// Tell whether there's a non-inline defun called 'name' in this program. +// If so, the reference to this name is a reference to a function, which +// will make variable references to it capture the program's function +// environment. +fn is_defun_in_codegen(compiler: &PrimaryCodegen, name: &[u8]) -> bool { + for h in compiler.original_helpers.iter() { + if matches!(h, HelperForm::Defun(false, _)) && h.name() == name { + return true; + } + } + + false +} + +// At the CLVM level, given a list of clvm expressios, make an expression +// that contains that list using conses. +fn make_list(loc: Srcloc, elements: Vec>) -> Rc { + let mut res = Rc::new(SExp::Nil(loc.clone())); + for e in elements.iter().rev() { + res = Rc::new(primcons(loc.clone(), e.clone(), res)); + } + res +} + +// +// Get the clvm expression that represents the indicated function as a +// callable value using the CLVM a operator. This value can be returned +// and even passed to another program because it carries the required +// environment to call functions it depends on from the call site. +// +// To do this, it writes an expression that conses the left env. +// +// (list (q . 2) (c (q . 1) n) (list (q . 4) (c (q . 1) 2) (q . 1))) +// +// Something like: +// (apply (quoted (expanded n)) (cons (quoted (expanded 2)) given-args)) +// +fn lambda_for_defun(loc: Srcloc, lookup: Rc) -> Rc { + let one_atom = Rc::new(SExp::Atom(loc.clone(), vec![1])); + let two_atom = Rc::new(SExp::Atom(loc.clone(), vec![2])); + let apply_atom = two_atom.clone(); + let cons_atom = Rc::new(SExp::Atom(loc.clone(), vec![4])); + make_list( + loc.clone(), + vec![ + Rc::new(primquote(loc.clone(), apply_atom)), + Rc::new(primcons( + loc.clone(), + Rc::new(primquote(loc.clone(), one_atom.clone())), + lookup, + )), + make_list( + loc.clone(), + vec![ + Rc::new(primquote(loc.clone(), cons_atom)), + Rc::new(primcons( + loc.clone(), + Rc::new(primquote(loc.clone(), one_atom.clone())), + two_atom, + )), + Rc::new(primquote(loc, one_atom)), + ], + ), + ], + ) +} + fn create_name_lookup( compiler: &PrimaryCodegen, l: Srcloc, name: &[u8], + // If the lookup is in head position, then it is a lookup as a callable, + // otherwise it's a lookup as a variable, which means that if a function + // is named, it will be built into an expression that allows it to be + // called by a CLVM 'a' operator as one would expect, regardless of how + // it integrates with the rest of the program it lives in. + as_variable: bool, ) -> Result, CompileErr> { compiler .constants .get(name) .map(|x| Ok(x.clone())) .unwrap_or_else(|| { - create_name_lookup_(l.clone(), name, compiler.env.clone(), compiler.env.clone()) - .map(|i| Rc::new(SExp::Integer(l.clone(), i.to_bigint().unwrap()))) + create_name_lookup_(l.clone(), name, compiler.env.clone(), compiler.env.clone()).map( + |i| { + // Determine if it's a defun. If so we can ensure that it's + // callable like a lambda by repeating the left env into it. + let find_program = Rc::new(SExp::Integer(l.clone(), i.to_bigint().unwrap())); + if as_variable && is_defun_in_codegen(compiler, name) { + // It's a defun. Harden the result so it is callable + // directly by the CLVM 'a' operator. + lambda_for_defun(l.clone(), find_program) + } else { + find_program + } + }, + ) }) } @@ -220,7 +306,9 @@ pub fn get_callable( SExp::Atom(l, name) => { let macro_def = compiler.macros.get(name); let inline = compiler.inlines.get(name); - let defun = create_name_lookup(compiler, l.clone(), name); + // We're getting a callable, so the access requested is not as + // a variable. + let defun = create_name_lookup(compiler, l.clone(), name, false); let prim = get_prim(l.clone(), compiler.prims.clone(), name); let atom_is_com = *name == "com".as_bytes().to_vec(); let atom_is_at = *name == "@".as_bytes().to_vec(); @@ -563,7 +651,10 @@ pub fn generate_expr_code( Rc::new(SExp::Integer(l.clone(), bi_one())), )) } else { - create_name_lookup(compiler, l.clone(), atom) + // This is as a variable access, given that we've got + // a Value bodyform containing an Atom, so if a defun + // is returned, it should be a packaged callable. + create_name_lookup(compiler, l.clone(), atom, true) .map(|f| Ok(CompiledCode(l.clone(), f))) .unwrap_or_else(|_| { // Pass through atoms that don't look up on behalf of @@ -1100,6 +1191,47 @@ pub fn hoist_body_let_binding( Rc::new(BodyForm::Call(l.clone(), new_call_list, new_tail)), )) } + BodyForm::Lambda(letdata) => { + // A lambda is exactly the same as + // 1) A function whose argument list is the captures plus the + // non-capture arguments. + // 2) A call site which includes a reference to the function + // surrounded with a structure that curries on the capture + // arguments. + + // Compose the function and return it as a desugared function. + // The functions desugared here also come from let bindings. + let new_function_args = Rc::new(SExp::Cons( + letdata.loc.clone(), + letdata.capture_args.clone(), + letdata.args.clone(), + )); + let new_function_name = gensym(b"lambda".to_vec()); + let (mut new_helpers_from_body, new_body) = hoist_body_let_binding( + Some(new_function_args.clone()), + new_function_args.clone(), + letdata.body.clone(), + )?; + let function = HelperForm::Defun( + false, + DefunData { + loc: letdata.loc.clone(), + name: new_function_name.clone(), + kw: letdata.kw.clone(), + nl: letdata.args.loc(), + orig_args: new_function_args.clone(), + args: new_function_args, + body: new_body, + }, + ); + new_helpers_from_body.push(function); + + // new_expr is the generated code at the call site. The reference + // to the actual function additionally is enriched by a left-env + // reference that gives it access to the program. + let new_expr = lambda_codegen(&new_function_name, letdata); + Ok((new_helpers_from_body, Rc::new(new_expr))) + } _ => Ok((Vec::new(), body.clone())), } } @@ -1257,6 +1389,7 @@ fn start_codegen( .set_code_generator(code_generator.clone()) .set_in_defun(false) .set_stdenv(false) + .set_start_env(None) .set_frontend_opt(false); let runner = context.runner(); @@ -1297,7 +1430,11 @@ fn start_codegen( }; code_generator.to_process = program.helpers.clone(); - code_generator.original_helpers = program.helpers.clone(); + // Ensure that we have the synthesis of the previous codegen's helpers and + // The ones provided with the new form if any. + let mut combined_helpers_for_codegen = program.helpers.clone(); + combined_helpers_for_codegen.append(&mut code_generator.original_helpers); + code_generator.original_helpers = combined_helpers_for_codegen; code_generator.final_expr = program.exp; Ok(code_generator) diff --git a/src/compiler/comptypes.rs b/src/compiler/comptypes.rs index 0d0c7a84e..009a5fdee 100644 --- a/src/compiler/comptypes.rs +++ b/src/compiler/comptypes.rs @@ -9,11 +9,19 @@ use clvm_rs::allocator::Allocator; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::compiler::clvm::sha256tree; +use crate::compiler::clvm::{sha256tree, truthy}; use crate::compiler::dialect::AcceptedDialect; use crate::compiler::sexp::{decode_string, enlist, SExp}; use crate::compiler::srcloc::Srcloc; +// Note: only used in tests, not normally dependencies. +#[cfg(test)] +use crate::compiler::compiler::DefaultCompilerOpts; +#[cfg(test)] +use crate::compiler::frontend::compile_bodyform; +#[cfg(test)] +use crate::compiler::sexp::parse_sexp; + /// The basic error type. It contains a Srcloc identifying coordinates of the /// error in the source file and a message. It probably should be made even better /// but this works ok. @@ -145,6 +153,17 @@ pub struct LetData { pub body: Rc, } +/// Describes a lambda used in an expression. +#[derive(Clone, Debug, Serialize)] +pub struct LambdaData { + pub loc: Srcloc, + pub kw: Option, + pub capture_args: Rc, + pub captures: Rc, + pub args: Rc, + pub body: Rc, +} + #[derive(Clone, Debug, Serialize)] pub enum BodyForm { /// A let or let* form (depending on LetFormKind). @@ -172,6 +191,17 @@ pub enum BodyForm { /// the compiled code. Here, it contains a CompileForm, which represents /// the full significant input of a program (yielded by frontend()). Mod(Srcloc, CompileForm), + /// A lambda form (lambda (...) ...) + /// + /// The lambda arguments are in two parts: + /// + /// (lambda ((& captures) real args) ...) + /// + /// Where the parts in captures are captured from the hosting environment. + /// Captures are optional. + /// The real args are given in the indicated shape when the lambda is applied + /// with the 'a' operator. + Lambda(Box), } /// The information needed to know about a defun. Whether it's inline is left in @@ -634,6 +664,35 @@ impl HelperForm { } } +fn compose_lambda_serialized_form(ldata: &LambdaData) -> Rc { + let lambda_kw = Rc::new(SExp::Atom(ldata.loc.clone(), b"lambda".to_vec())); + let amp_kw = Rc::new(SExp::Atom(ldata.loc.clone(), b"&".to_vec())); + let arguments = if truthy(ldata.capture_args.clone()) { + Rc::new(SExp::Cons( + ldata.loc.clone(), + Rc::new(SExp::Cons( + ldata.loc.clone(), + amp_kw, + ldata.capture_args.clone(), + )), + ldata.args.clone(), + )) + } else { + ldata.args.clone() + }; + let rest_of_body = Rc::new(SExp::Cons( + ldata.loc.clone(), + ldata.body.to_sexp(), + Rc::new(SExp::Nil(ldata.loc.clone())), + )); + + Rc::new(SExp::Cons( + ldata.loc.clone(), + lambda_kw, + Rc::new(SExp::Cons(ldata.loc.clone(), arguments, rest_of_body)), + )) +} + fn compose_let(marker: &[u8], letdata: &LetData) -> Rc { let translated_bindings: Vec> = letdata.bindings.iter().map(|x| x.to_sexp()).collect(); let bindings_cons = list_to_cons(letdata.loc.clone(), &translated_bindings); @@ -696,6 +755,7 @@ impl BodyForm { BodyForm::Call(loc, _, _) => loc.clone(), BodyForm::Value(a) => a.loc(), BodyForm::Mod(kl, program) => kl.ext(&program.loc), + BodyForm::Lambda(ldata) => ldata.loc.ext(&ldata.body.loc()), } } @@ -728,10 +788,35 @@ impl BodyForm { Rc::new(SExp::Atom(loc.clone(), b"mod".to_vec())), program.to_sexp(), )), + BodyForm::Lambda(ldata) => compose_lambda_serialized_form(ldata), } } } +// Note: in cfg(test), this will not be part of the finished binary. +// Also: not a test in itself, just named test so for at least some readers, +// its association with test infrastructure will be apparent. +#[cfg(test)] +fn test_parse_bodyform_to_frontend(bf: &str) { + let name = "*test*"; + let loc = Srcloc::start(name); + let opts = Rc::new(DefaultCompilerOpts::new(name)); + let parsed = parse_sexp(loc, bf.bytes()).expect("should parse"); + let bodyform = compile_bodyform(opts, parsed[0].clone()).expect("should compile"); + assert_eq!(bodyform.to_sexp(), parsed[0]); +} + +// Inline unit tests for sexp serialization. +#[test] +fn test_mod_serialize_regular_mod() { + test_parse_bodyform_to_frontend("(mod (X) (+ X 1))"); +} + +#[test] +fn test_mod_serialize_simple_lambda() { + test_parse_bodyform_to_frontend("(lambda (X) (+ X 1))"); +} + impl Binding { /// Express the binding as it would be used in a let form. pub fn to_sexp(&self) -> Rc { diff --git a/src/compiler/evaluate.rs b/src/compiler/evaluate.rs index fc5fac818..4faa79259 100644 --- a/src/compiler/evaluate.rs +++ b/src/compiler/evaluate.rs @@ -9,12 +9,12 @@ use clvm_rs::allocator::Allocator; use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::compiler::clvm::run; +use crate::compiler::clvm::{run, truthy}; use crate::compiler::codegen::{codegen, hoist_assign_form}; use crate::compiler::compiler::is_at_capture; use crate::compiler::comptypes::{ - Binding, BindingPattern, BodyForm, CallSpec, CompileErr, CompileForm, CompilerOpts, HelperForm, - LetData, LetFormInlineHint, LetFormKind, + Binding, BindingPattern, BodyForm, CallSpec, CompileErr, CompileForm, CompilerOpts, DefunData, + HelperForm, LambdaData, LetData, LetFormInlineHint, LetFormKind, }; use crate::compiler::frontend::frontend; use crate::compiler::runtypes::RunFailure; @@ -64,6 +64,12 @@ impl<'info> VisitedInfoAccess for VisitedMarker<'info, VisitedInfo> { } } +pub struct LambdaApply { + lambda: LambdaData, + body: Rc, + env: Rc, +} + // Frontend evaluator based on my fuzzer representation and direct interpreter of // that. #[derive(Debug)] @@ -329,6 +335,16 @@ fn arg_inputs_primitive(arginputs: Rc) -> bool { } } +fn decons_args(formed_tail: Rc) -> ArgInputs { + if let Some((head, tail)) = match_cons(formed_tail.clone()) { + let arg_head = decons_args(head.clone()); + let arg_tail = decons_args(tail.clone()); + ArgInputs::Pair(Rc::new(arg_head), Rc::new(arg_tail)) + } else { + ArgInputs::Whole(formed_tail) + } +} + fn build_argument_captures( l: &Srcloc, arguments_to_convert: &[Rc], @@ -336,7 +352,7 @@ fn build_argument_captures( args: Rc, ) -> Result, Rc>, CompileErr> { let formed_tail = tail.unwrap_or_else(|| Rc::new(BodyForm::Quoted(SExp::Nil(l.clone())))); - let mut formed_arguments = ArgInputs::Whole(formed_tail); + let mut formed_arguments = decons_args(formed_tail); for i_reverse in 0..arguments_to_convert.len() { let i = arguments_to_convert.len() - i_reverse - 1; @@ -652,6 +668,27 @@ pub fn eval_dont_expand_let(inline_hint: &Option) -> bool { matches!(inline_hint, Some(LetFormInlineHint::NonInline(_))) } +pub fn filter_capture_args(args: Rc, name_map: &HashMap, Rc>) -> Rc { + match args.borrow() { + SExp::Cons(l, a, b) => { + let a_filtered = filter_capture_args(a.clone(), name_map); + let b_filtered = filter_capture_args(b.clone(), name_map); + if !truthy(a_filtered.clone()) && !truthy(b_filtered.clone()) { + return Rc::new(SExp::Nil(l.clone())); + } + Rc::new(SExp::Cons(l.clone(), a_filtered, b_filtered)) + } + SExp::Atom(l, n) => { + if name_map.contains_key(n) { + Rc::new(SExp::Nil(l.clone())) + } else { + args + } + } + _ => Rc::new(SExp::Nil(args.loc())), + } +} + impl<'info> Evaluator { pub fn new( opts: Rc, @@ -734,11 +771,98 @@ impl<'info> Evaluator { } } + fn is_lambda_apply( + &self, + allocator: &mut Allocator, + visited_: &'info mut VisitedMarker<'_, VisitedInfo>, + prog_args: Rc, + env: &HashMap, Rc>, + parts: &[Rc], + only_inline: bool, + ) -> Result, CompileErr> { + if parts.len() == 3 && is_apply_atom(parts[0].to_sexp()) { + let mut visited = VisitedMarker::again(parts[0].loc(), visited_)?; + let evaluated_prog = self.shrink_bodyform_visited( + allocator, + &mut visited, + prog_args.clone(), + env, + parts[1].clone(), + only_inline, + )?; + let evaluated_env = self.shrink_bodyform_visited( + allocator, + &mut visited, + prog_args, + env, + parts[2].clone(), + only_inline, + )?; + if let BodyForm::Lambda(ldata) = evaluated_prog.borrow() { + return Ok(Some(LambdaApply { + lambda: *ldata.clone(), + body: ldata.body.clone(), + env: evaluated_env, + })); + } + } + + Ok(None) + } + + fn do_lambda_apply( + &self, + allocator: &mut Allocator, + visited: &mut VisitedMarker<'info, VisitedInfo>, + prog_args: Rc, + env: &HashMap, Rc>, + lapply: &LambdaApply, + only_inline: bool, + ) -> Result, CompileErr> { + let mut lambda_env = env.clone(); + + // Finish eta-expansion. + + // We're carrying an enriched environment which we can use to enrich + // the env map at this time. Once we do that we can expand the body + // fully because we're carring the info that goes with the primary + // arguments. + // + // Generate the enriched environment. + let reified_captures = self.shrink_bodyform_visited( + allocator, + visited, + prog_args, + env, + lapply.lambda.captures.clone(), + only_inline, + )?; + let formed_caps = ArgInputs::Whole(reified_captures); + create_argument_captures( + &mut lambda_env, + &formed_caps, + lapply.lambda.capture_args.clone(), + )?; + + // Create captures with the actual parameters. + let formed_args = ArgInputs::Whole(lapply.env.clone()); + create_argument_captures(&mut lambda_env, &formed_args, lapply.lambda.args.clone())?; + + self.shrink_bodyform_visited( + allocator, + visited, + lapply.lambda.args.clone(), + &lambda_env, + lapply.body.clone(), + only_inline, + ) + } + #[allow(clippy::too_many_arguments)] fn invoke_primitive( &self, allocator: &mut Allocator, - visited: &mut VisitedMarker<'info, VisitedInfo>, + visited_: &'info mut VisitedMarker<'_, VisitedInfo>, call: &CallSpec, prog_args: Rc, arguments_to_convert: &[Rc], @@ -747,6 +871,7 @@ impl<'info> Evaluator { ) -> Result, CompileErr> { let mut all_primitive = true; let mut target_vec: Vec> = call.args.to_owned(); + let mut visited = VisitedMarker::again(call.loc.clone(), visited_)?; if call.name == "@".as_bytes() { // Synthesize the environment for this function @@ -786,7 +911,7 @@ impl<'info> Evaluator { let i = arguments_to_convert.len() - i_reverse - 1; let shrunk = self.shrink_bodyform_visited( allocator, - visited, + &mut visited, prog_args.clone(), env, arguments_to_convert[i].clone(), @@ -823,11 +948,27 @@ impl<'info> Evaluator { } } } + } else if let Some(applied_lambda) = self.is_lambda_apply( + allocator, + &mut visited, + prog_args.clone(), + env, + &target_vec, + only_inline, + )? { + self.do_lambda_apply( + allocator, + &mut visited, + prog_args.clone(), + env, + &applied_lambda, + only_inline, + ) } else { // Since this is a primitive, there's no tail transform. let reformed = BodyForm::Call(call.loc.clone(), target_vec.clone(), call.tail.clone()); - self.chase_apply(allocator, visited, Rc::new(reformed)) + self.chase_apply(allocator, &mut visited, Rc::new(reformed)) } }) .unwrap_or_else(|| { @@ -998,10 +1139,23 @@ impl<'info> Evaluator { return Ok(call.original.clone()); } + let translated_tail = if let Some(t) = call.tail.as_ref() { + Some(self.shrink_bodyform_visited( + allocator, + visited, + prog_args.clone(), + env, + t.clone(), + only_inline, + )?) + } else { + None + }; + let argument_captures_untranslated = build_argument_captures( &call.loc.clone(), arguments_to_convert, - call.tail.clone(), + translated_tail.clone(), defun.args.clone(), )?; @@ -1044,6 +1198,101 @@ impl<'info> Evaluator { } } + fn enrich_lambda_site_info( + &self, + allocator: &mut Allocator, + visited: &'info mut VisitedMarker<'_, VisitedInfo>, + prog_args: Rc, + env: &HashMap, Rc>, + ldata: &LambdaData, + only_inline: bool, + ) -> Result, CompileErr> { + if !truthy(ldata.capture_args.clone()) { + return Ok(Rc::new(BodyForm::Lambda(Box::new(ldata.clone())))); + } + + // Rewrite the captures based on what we know at the call site. + let new_captures = self.shrink_bodyform_visited( + allocator, + visited, + prog_args.clone(), + env, + ldata.captures.clone(), + only_inline, + )?; + + // Break up and make binding map. + let deconsed_args = decons_args(new_captures.clone()); + let mut arg_captures = HashMap::new(); + create_argument_captures( + &mut arg_captures, + &deconsed_args, + ldata.capture_args.clone(), + )?; + + // Filter out elements that are not interpretable yet. + let mut interpretable_captures = HashMap::new(); + for (n, v) in arg_captures.iter() { + if dequote(v.loc(), v.clone()).is_ok() { + // This capture has already been made into a literal. + // We will substitute it in the lambda body and remove it + // from the capture set. + interpretable_captures.insert(n.clone(), v.clone()); + } + } + + let combined_args = Rc::new(SExp::Cons( + ldata.loc.clone(), + ldata.capture_args.clone(), + ldata.args.clone(), + )); + + // Eliminate the captures via beta substituion. + let simplified_body = self.shrink_bodyform_visited( + allocator, + visited, + combined_args.clone(), + &interpretable_captures, + ldata.body.clone(), + only_inline, + )?; + + let new_capture_args = + filter_capture_args(ldata.capture_args.clone(), &interpretable_captures); + Ok(Rc::new(BodyForm::Lambda(Box::new(LambdaData { + args: ldata.args.clone(), + capture_args: new_capture_args, + captures: new_captures, + body: simplified_body, + ..ldata.clone() + })))) + } + + fn get_function(&self, name: &[u8]) -> Option> { + for h in self.helpers.iter() { + if let HelperForm::Defun(false, dd) = &h { + if name == h.name() { + return Some(Box::new(dd.clone())); + } + } + } + + None + } + + fn create_mod_for_fun(&self, l: &Srcloc, function: &DefunData) -> Rc { + Rc::new(BodyForm::Mod( + l.clone(), + CompileForm { + loc: l.clone(), + include_forms: Vec::new(), + args: function.args.clone(), + helpers: self.helpers.clone(), + exp: function.body.clone(), + }, + )) + } + // A frontend language evaluator and minifier fn shrink_bodyform_visited( &self, @@ -1134,6 +1383,15 @@ impl<'info> Evaluator { literal_args, only_inline, ) + } else if let Some(function) = self.get_function(name) { + self.shrink_bodyform_visited( + allocator, + &mut visited, + prog_args, + env, + self.create_mod_for_fun(l, function.borrow()), + only_inline, + ) } else { env.get(name) .map(|x| { @@ -1229,6 +1487,14 @@ impl<'info> Evaluator { let code = codegen(&mut context_wrapper.context, self.opts.clone(), program)?; Ok(Rc::new(BodyForm::Quoted(code))) } + BodyForm::Lambda(ldata) => self.enrich_lambda_site_info( + allocator, + &mut visited, + prog_args, + env, + ldata, + only_inline, + ), } } diff --git a/src/compiler/frontend.rs b/src/compiler/frontend.rs index ad24d0a24..aa8cd7ace 100644 --- a/src/compiler/frontend.rs +++ b/src/compiler/frontend.rs @@ -9,8 +9,9 @@ use crate::compiler::comptypes::{ CompilerOpts, ConstantKind, DefconstData, DefmacData, DefunData, HelperForm, IncludeDesc, LetData, LetFormInlineHint, LetFormKind, ModAccum, }; +use crate::compiler::lambda::handle_lambda; use crate::compiler::preprocessor::preprocess; -use crate::compiler::rename::{rename_assign_bindings, rename_children_compileform}; +use crate::compiler::rename::rename_children_compileform; use crate::compiler::sexp::{decode_string, enlist, SExp}; use crate::compiler::srcloc::Srcloc; use crate::util::u8_from_number; @@ -69,6 +70,11 @@ fn collect_used_names_bodyform(body: &BodyForm) -> Vec> { result } BodyForm::Mod(_, _) => vec![], + BodyForm::Lambda(ldata) => { + let mut capture_names = collect_used_names_bodyform(ldata.captures.borrow()); + capture_names.append(&mut collect_used_names_bodyform(ldata.body.borrow())); + capture_names + } } } @@ -293,10 +299,6 @@ pub fn make_provides_set(provides_set: &mut HashSet>, body_sexp: Rc) -> bool { - opts.dialect().stepping.unwrap_or(0) > 22 -} - fn handle_assign_form( opts: Rc, l: Srcloc, @@ -340,19 +342,12 @@ fn handle_assign_form( })); } - let mut compiled_body = compile_bodyform(opts.clone(), Rc::new(v[v.len() - 1].clone()))?; + let compiled_body = compile_bodyform(opts.clone(), Rc::new(v[v.len() - 1].clone()))?; // We don't need to do much if there were no bindings. if bindings.is_empty() { return Ok(compiled_body); } - if at_or_above_23(opts.clone()) { - let (new_compiled_body, new_bindings) = - rename_assign_bindings(&l, &bindings, Rc::new(compiled_body))?; - compiled_body = new_compiled_body; - bindings = new_bindings; - }; - // Return a precise representation of this assign while storing up the work // we did breaking it down. Ok(BodyForm::Let( @@ -401,9 +396,7 @@ pub fn compile_bodyform( match op.borrow() { SExp::Atom(l, atom_name) => { - if *atom_name == "q".as_bytes().to_vec() - || (atom_name.len() == 1 && atom_name[0] == 1) - { + if *atom_name == b"q" || (atom_name.len() == 1 && atom_name[0] == 1) { let tail_copy: &SExp = tail.borrow(); return Ok(BodyForm::Quoted(tail_copy.clone())); } @@ -413,14 +406,12 @@ pub fn compile_bodyform( match tail.proper_list() { Some(v) => { - if *atom_name == "let".as_bytes().to_vec() - || *atom_name == "let*".as_bytes().to_vec() - { + if *atom_name == b"let" || *atom_name == b"let*" { if v.len() != 2 { return finish_err("let"); } - let kind = if *atom_name == "let".as_bytes().to_vec() { + let kind = if *atom_name == b"let" { LetFormKind::Parallel } else { LetFormKind::Sequential @@ -466,7 +457,7 @@ pub fn compile_bodyform( let quote_body = v[0].clone(); Ok(BodyForm::Quoted(quote_body)) - } else if *atom_name == "qq".as_bytes().to_vec() { + } else if *atom_name == b"qq" { if v.len() != 1 { return finish_err("qq"); } @@ -474,9 +465,11 @@ pub fn compile_bodyform( let quote_body = v[0].clone(); qq_to_expression(opts, Rc::new(quote_body)) - } else if *atom_name == "mod".as_bytes().to_vec() { + } else if *atom_name == b"mod" { let subparse = frontend(opts, &[body.clone()])?; Ok(BodyForm::Mod(op.loc(), subparse)) + } else if *atom_name == b"lambda" { + handle_lambda(opts, Some(l.clone()), &v) } else { application() } @@ -859,7 +852,7 @@ fn frontend_start( )); } - if *mod_atom == "mod".as_bytes().to_vec() { + if *mod_atom == b"mod" { let args = Rc::new(x[1].clone()); let body_vec: Vec> = x.iter().skip(2).map(|s| Rc::new(s.clone())).collect(); diff --git a/src/compiler/inline.rs b/src/compiler/inline.rs index c60a6e511..c977e65ba 100644 --- a/src/compiler/inline.rs +++ b/src/compiler/inline.rs @@ -10,7 +10,7 @@ use crate::compiler::codegen::{generate_expr_code, get_call_name, get_callable}; use crate::compiler::compiler::is_at_capture; use crate::compiler::comptypes::{ ArgsAndTail, BodyForm, CallSpec, Callable, CompileErr, CompiledCode, CompilerOpts, - InlineFunction, PrimaryCodegen, + InlineFunction, LambdaData, PrimaryCodegen, }; use crate::compiler::sexp::{decode_string, SExp}; use crate::compiler::srcloc::Srcloc; @@ -445,6 +445,24 @@ fn replace_inline_body( .unwrap_or_else(|| expr.clone()); Ok(alookup) } + BodyForm::Lambda(ldata) => { + let rewritten_captures = replace_inline_body( + visited_inlines, + runner, + opts, + compiler, + loc, + inline, + args, + tail, + callsite, + ldata.captures.clone(), + )?; + Ok(Rc::new(BodyForm::Lambda(Box::new(LambdaData { + captures: rewritten_captures, + ..*ldata.clone() + })))) + } _ => Ok(expr.clone()), } } diff --git a/src/compiler/lambda.rs b/src/compiler/lambda.rs new file mode 100644 index 000000000..1ce93538f --- /dev/null +++ b/src/compiler/lambda.rs @@ -0,0 +1,155 @@ +use std::borrow::Borrow; +use std::rc::Rc; + +use crate::compiler::clvm::truthy; +use crate::compiler::comptypes::{BodyForm, CompileErr, CompilerOpts, LambdaData}; +use crate::compiler::frontend::compile_bodyform; +use crate::compiler::sexp::SExp; +use crate::compiler::srcloc::Srcloc; + +fn make_captures(opts: Rc, sexp: Rc) -> Result, CompileErr> { + if let SExp::Cons(l, f, r) = sexp.borrow() { + Ok(Rc::new(make_operator( + l.clone(), + 4, + make_captures(opts.clone(), f.clone())?, + make_captures(opts, r.clone())?, + ))) + } else if !truthy(sexp.clone()) { + Ok(Rc::new(BodyForm::Quoted(SExp::Nil(sexp.loc())))) + } else { + Ok(Rc::new(compile_bodyform(opts, sexp)?)) + } +} + +struct FoundLambdaCaptures { + args: Rc, + capture_args: Rc, + captures: Rc, +} + +fn find_and_compose_captures( + opts: Rc, + sexp: &SExp, +) -> Result { + let mut found = FoundLambdaCaptures { + args: Rc::new(sexp.clone()), + capture_args: Rc::new(SExp::Nil(sexp.loc())), + captures: Rc::new(BodyForm::Quoted(SExp::Nil(sexp.loc()))), + }; + if let SExp::Cons(_, l, r) = sexp { + if let SExp::Cons(_, head, rest) = l.borrow() { + if let SExp::Atom(_, name) = head.borrow() { + if name == b"&" { + found.args = r.clone(); + found.capture_args = rest.clone(); + found.captures = make_captures(opts, rest.clone())?; + } + } + } + } + + Ok(found) +} + +fn make_operator(loc: Srcloc, op: u8, arg1: Rc, arg2: Rc) -> BodyForm { + BodyForm::Call( + loc.clone(), + vec![ + Rc::new(BodyForm::Value(SExp::Atom(loc, vec![op]))), + arg1, + arg2, + ], + // Calling a primitive, no tail. + None, + ) +} + +fn make_cons(loc: Srcloc, arg1: Rc, arg2: Rc) -> BodyForm { + make_operator(loc, 4, arg1, arg2) +} + +fn make_list(loc: Srcloc, args: &[BodyForm]) -> BodyForm { + let mut res = BodyForm::Quoted(SExp::Nil(loc.clone())); + let cons_atom = BodyForm::Value(SExp::Atom(loc.clone(), vec![4])); + for a in args.iter().rev() { + res = BodyForm::Call( + loc.clone(), + vec![Rc::new(cons_atom.clone()), Rc::new(a.clone()), Rc::new(res)], + // Calling a primitive, no tail. + None, + ); + } + res +} + +// +// Lambda +// +// (lambda ((& captures) arguments) +// (body) +// ) +// +// Yields: +// +// (list 2 +// (c 1 ) +// (list 4 (list 4 (c 1 compose_captures) @)) +// ) +// +pub fn lambda_codegen(name: &[u8], ldata: &LambdaData) -> BodyForm { + // Code to retrieve and quote the captures. + let quote_atom = BodyForm::Value(SExp::Atom(ldata.loc.clone(), vec![1])); + let apply_atom = BodyForm::Value(SExp::Atom(ldata.loc.clone(), vec![2])); + let cons_atom = BodyForm::Value(SExp::Atom(ldata.loc.clone(), vec![4])); + let whole_env = quote_atom.clone(); + + let compose_captures = make_cons( + ldata.loc.clone(), + Rc::new(quote_atom.clone()), + ldata.captures.clone(), + ); + + make_list( + ldata.loc.clone(), + &[ + apply_atom, + make_cons( + ldata.loc.clone(), + Rc::new(quote_atom), + Rc::new(BodyForm::Value(SExp::Atom( + ldata.loc.clone(), + name.to_vec(), + ))), + ), + make_list(ldata.loc.clone(), &[cons_atom, compose_captures, whole_env]), + ], + ) +} + +pub fn handle_lambda( + opts: Rc, + kw_loc: Option, + v: &[SExp], +) -> Result { + if v.len() < 2 { + return Err(CompileErr( + v[0].loc(), + "Must provide at least arguments and body to lambda".to_string(), + )); + } + + let found = find_and_compose_captures(opts.clone(), &v[0])?; + + // Requires captures + let subparse = compile_bodyform(opts, Rc::new(v[1].clone()))?; + + Ok(BodyForm::Lambda(Box::new(LambdaData { + loc: v[0].loc(), + kw: kw_loc, + args: found.args.clone(), + capture_args: found.capture_args.clone(), + captures: found.captures, + body: Rc::new(subparse), + }))) +} diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index c6f298712..d3159d72e 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -20,6 +20,7 @@ pub mod evaluate; pub mod frontend; pub mod gensym; mod inline; +mod lambda; pub mod optimize; pub mod preprocessor; pub mod prims; diff --git a/src/compiler/rename.rs b/src/compiler/rename.rs index 833706754..743805e97 100644 --- a/src/compiler/rename.rs +++ b/src/compiler/rename.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use crate::compiler::codegen::toposort_assign_bindings; use crate::compiler::comptypes::{ map_m, map_m_reverse, Binding, BindingPattern, BodyForm, CompileErr, CompileForm, DefconstData, - DefmacData, DefunData, HelperForm, LetData, LetFormKind, + DefmacData, DefunData, HelperForm, LambdaData, LetData, LetFormKind, }; use crate::compiler::gensym::gensym; use crate::compiler::sexp::SExp; @@ -234,12 +234,18 @@ fn rename_in_bodyform( }, BodyForm::Call(l, vs, tail) => { - let new_vs = map_m( + let mut new_vs = map_m( &|x: &Rc| -> Result, CompileErr> { Ok(Rc::new(rename_in_bodyform(namemap, x.clone())?)) }, vs, )?; + // Ensure that we haven't renamed the 0th element of a call + // They exist in a separate (global) namespace of callables + // and aren't in the variable scope stack. + if !vs.is_empty() { + new_vs[0] = vs[0].clone(); + } let new_tail = if let Some(t) = tail.as_ref() { Some(Rc::new(rename_in_bodyform(namemap, t.clone())?)) } else { @@ -249,6 +255,21 @@ fn rename_in_bodyform( } BodyForm::Mod(l, prog) => Ok(BodyForm::Mod(l.clone(), prog.clone())), + // Rename lambda arguments down the lexical scope. + BodyForm::Lambda(ldata) => { + let renamed_capture_inputs = + Rc::new(rename_in_bodyform(namemap, ldata.captures.clone())?); + let renamed_capture_outputs = + rename_in_cons(namemap, ldata.capture_args.clone(), false); + let renamed_body = Rc::new(rename_args_bodyform(ldata.body.borrow())?); + let outer_renamed_body = rename_in_bodyform(namemap, renamed_body)?; + Ok(BodyForm::Lambda(Box::new(LambdaData { + captures: renamed_capture_inputs, + capture_args: renamed_capture_outputs, + body: Rc::new(outer_renamed_body), + ..*ldata.clone() + }))) + } } } @@ -362,6 +383,20 @@ fn rename_args_bodyform(b: &BodyForm) -> Result { Ok(BodyForm::Call(l.clone(), new_vs, new_tail)) } BodyForm::Mod(l, program) => Ok(BodyForm::Mod(l.clone(), program.clone())), + BodyForm::Lambda(ldata) => { + let mut own_args = HashMap::new(); + for (n, v) in invent_new_names_sexp(ldata.args.clone()).iter() { + own_args.insert(n.clone(), v.clone()); + } + let new_args = rename_in_cons(&own_args, ldata.args.clone(), false); + let new_body = rename_args_bodyform(ldata.body.borrow())?; + let renamed_with_own_args = rename_in_bodyform(&own_args, Rc::new(new_body))?; + Ok(BodyForm::Lambda(Box::new(LambdaData { + args: new_args, + body: Rc::new(renamed_with_own_args), + ..*ldata.clone() + }))) + } } } diff --git a/src/tests/classic/run.rs b/src/tests/classic/run.rs index 42b66546c..b7f038fc1 100644 --- a/src/tests/classic/run.rs +++ b/src/tests/classic/run.rs @@ -530,7 +530,7 @@ fn test_treehash_constant_embedded_modern_loop() { .trim() .to_string(); eprintln!("{result_text}"); - assert!(result_text.starts_with("*command*")); + // Asserting where the stack overflows isn't necessary. assert!(result_text.contains("stack limit exceeded")); } @@ -974,6 +974,39 @@ fn test_modern_sets_source_file_in_symbols() { ); } +// Test that leaving off the lambda captures causes bare words for the +// requested values to find their way into the output and that having +// the capture catches it. This shows that uses of uncaptured words +// are unencumbered. +#[test] +fn test_lambda_without_capture_reproduces_bare_word_in_output() { + let compiled = do_basic_run(&vec![ + "run".to_string(), + "-i".to_string(), + "resources/tests".to_string(), + "resources/tests/rps-referee-uncaptured.clsp".to_string(), + ]) + .trim() + .to_string(); + assert!(compiled.contains("AMOUNT")); + assert!(compiled.contains("new_puzzle_hash")); +} + +// Test that having a lambda capture captures all the associated words. +#[test] +fn test_lambda_with_capture_defines_word() { + let compiled = do_basic_run(&vec![ + "run".to_string(), + "-i".to_string(), + "resources/tests".to_string(), + "resources/tests/rps-referee.clsp".to_string(), + ]) + .trim() + .to_string(); + assert!(!compiled.contains("AMOUNT")); + assert!(!compiled.contains("new_puzzle_hash")); +} + #[test] fn test_assign_lambda_code_generation() { let tname = "test_assign_lambda_code_generation.sym".to_string(); diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index ea916eb48..9a38cfb34 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -1471,6 +1471,393 @@ fn test_inline_out_of_bounds_diagnostic() { } } +#[test] +fn test_lambda_without_capture_from_function() { + let prog = indoc! {" +(mod (A B) + (include *standard-cl-21*) + (defun FOO () (lambda (X Y) (+ X Y))) + (a (FOO) (list A B)) + )"} + .to_string(); + let res = run_string(&prog, &"(3 4)".to_string()).unwrap(); + assert_eq!(res.to_string(), "7"); +} + +#[test] +fn test_lambda_without_capture() { + let prog = indoc! {" +(mod (A B) + (include *standard-cl-21*) + (a (lambda (X Y) (+ X Y)) (list A B)) + )"} + .to_string(); + let res = run_string(&prog, &"(3 4)".to_string()).unwrap(); + assert_eq!(res.to_string(), "7"); +} + +#[test] +fn test_lambda_with_capture_from_function() { + let prog = indoc! {" +(mod (A B) + (include *standard-cl-21*) + (defun FOO (Z) (lambda ((& Z) X) (- X Z))) + (a (FOO A) (list B)) + )"} + .to_string(); + let res = run_string(&prog, &"(5 19)".to_string()).unwrap(); + assert_eq!(res.to_string(), "14"); +} + +#[test] +fn test_lambda_with_capture() { + let prog = indoc! {" +(mod (A B) + (include *standard-cl-21*) + (a (lambda ((& A) Y) (- Y A)) (list B)) + )"} + .to_string(); + let res = run_string(&prog, &"(5 19)".to_string()).unwrap(); + assert_eq!(res.to_string(), "14"); +} + +#[test] +fn test_lambda_in_let_0() { + let prog = indoc! {" +(mod (A) + (include *standard-cl-21*) + (defun FOO (Z) + (let ((Q (* 2 Z))) + (lambda ((& Q)) (- 100 Q)) + ) + ) + (a (FOO A) ()) + )"} + .to_string(); + let res = run_string(&prog, &"(5)".to_string()).unwrap(); + assert_eq!(res.to_string(), "90"); +} + +#[test] +fn test_lambda_in_let_1() { + let prog = indoc! {" +(mod (A B) + (include *standard-cl-21*) + (defun FOO (Z) + (let ((Q (* 2 Z))) + (lambda ((& Q) X) (- X Q)) + ) + ) + (a (FOO A) (list B)) + )"} + .to_string(); + let res = run_string(&prog, &"(5 19)".to_string()).unwrap(); + assert_eq!(res.to_string(), "9"); +} + +#[test] +fn test_lambda_in_map() { + let prog = indoc! {" +(mod (add-number L) + + (include *standard-cl-21*) + + (defun map (F L) + (if L + (c (a F (list (f L))) (map F (r L))) + () + ) + ) + + (map + (lambda ((& add-number) number) (+ add-number number)) + L + ) + ) +"} + .to_string(); + let res = run_string(&prog, &"(5 (1 2 3 4))".to_string()).unwrap(); + assert_eq!(res.to_string(), "(6 7 8 9)"); +} + +#[test] +fn test_lambda_in_map_with_let_surrounding() { + let prog = indoc! {" +(mod (add-number L) + + (include *standard-cl-21*) + + (defun map (F L) + (if L + (c (a F (list (f L))) (map F (r L))) + () + ) + ) + + (map + (let ((A (* add-number 2))) + (lambda ((& A) number) (+ A number)) + ) + L + ) + ) +"} + .to_string(); + let res = run_string(&prog, &"(5 (1 2 3 4))".to_string()).unwrap(); + assert_eq!(res.to_string(), "(11 12 13 14)"); +} + +#[test] +fn test_map_with_lambda_function_from_env_and_bindings() { + let prog = indoc! {" + (mod (add-number L) + + (include *standard-cl-21*) + + (defun map (F L) + (if L + (c (a F (list (f L))) (map F (r L))) + () + ) + ) + + (defun add-twice (X Y) (+ (* 2 X) Y)) + + (map + (lambda ((& add-number) number) (add-twice add-number number)) + L + ) + )"} + .to_string(); + let res = run_string(&prog, &"(5 (1 2 3 4))".to_string()).unwrap(); + assert_eq!(res.to_string(), "(11 12 13 14)"); +} + +#[test] +fn test_map_with_lambda_function_from_env_no_bindings() { + let prog = indoc! {" + (mod (L) + + (include *standard-cl-21*) + + (defun map (F L) + (if L + (c (a F (list (f L))) (map F (r L))) + () + ) + ) + + (defun sum-list (L) + (if L + (+ (f L) (sum-list (r L))) + () + ) + ) + + (map + (lambda (lst) (sum-list lst)) + L + ) + )"} + .to_string(); + let res = run_string(&prog, &"(((5 10 15) (2 4 8) (3 6 9)))".to_string()).unwrap(); + assert_eq!(res.to_string(), "(30 14 18)"); +} + +#[test] +fn test_lambda_using_let() { + let prog = indoc! {" + (mod (P L) + + (include *standard-cl-21*) + + (defun map (F L) + (if L + (c (a F (list (f L))) (map F (r L))) + () + ) + ) + + (map + (lambda ((& P) item) (let ((composed (c P item))) composed)) + L + ) + )"} + .to_string(); + let res = run_string(&prog, &"(1 (10 20 30))".to_string()).unwrap(); + assert_eq!(res.to_string(), "((1 . 10) (1 . 20) (1 . 30))"); +} + +#[test] +fn test_lambda_using_macro() { + let prog = indoc! {" + (mod (P L) + + (include *standard-cl-21*) + + (defun map (F L) + (if L + (c (a F (list (f L))) (map F (r L))) + () + ) + ) + + (map + (lambda ((& P) item) (list P item)) + L + ) + )"} + .to_string(); + let res = run_string(&prog, &"(1 (10 20 30))".to_string()).unwrap(); + assert_eq!(res.to_string(), "((1 10) (1 20) (1 30))"); +} + +#[test] +fn test_lambda_reduce() { + let prog = indoc! {" + (mod (LST) + (include *standard-cl-21*) + (defun reduce (fun lst init) + (if lst + (reduce fun (r lst) (a fun (list (f lst) init))) + init + ) + ) + + (let + ((capture 100)) + (reduce (lambda ((& capture) (X Y) ACC) (+ (* X Y) ACC capture)) LST 0) + ) + ) + "} + .to_string(); + let res = run_string(&prog, &"(((2 3) (4 9)))".to_string()).unwrap(); + assert_eq!(res.to_string(), "242"); +} + +#[test] +fn test_lambda_as_let_binding() { + let prog = indoc! {" + (mod (P L) + (defun map (F L) + (if L (c (a F (list (f L))) (map F (r L))) ()) + ) + (defun x2 (N) (* 2 N)) + (defun x3p1 (N) (+ 1 (* 3 N))) + (let* ((H (lambda (N) (x2 N))) + (G (lambda (N) (x3p1 N))) + (F (if P G H))) + (map F L) + ) + ) + "} + .to_string(); + let res0 = run_string(&prog, &"(0 (1 2 3))".to_string()).unwrap(); + assert_eq!(res0.to_string(), "(2 4 6)"); + let res1 = run_string(&prog, &"(1 (1 2 3))".to_string()).unwrap(); + assert_eq!(res1.to_string(), "(4 7 10)"); +} + +#[test] +fn test_lambda_mixed_let_binding() { + let prog = indoc! {" + (mod (P L) + (defun map (F L) + (if L (c (a F (list (f L))) (map F (r L))) ()) + ) + (defun x2 (N) (* 2 N)) + (defun x3p1 (N) (+ 1 (* 3 N))) + (let* ((G (lambda (N) (x3p1 N))) + (F (if P G (lambda (N) (x2 N))))) + (map F L) + ) + ) + "} + .to_string(); + let res0 = run_string(&prog, &"(0 (1 2 3))".to_string()).unwrap(); + assert_eq!(res0.to_string(), "(2 4 6)"); + let res1 = run_string(&prog, &"(1 (1 2 3))".to_string()).unwrap(); + assert_eq!(res1.to_string(), "(4 7 10)"); +} + +#[test] +fn test_lambda_hof_1() { + let prog = indoc! {" + (mod (P) + (a (a (lambda ((& P) X) (lambda ((& P X)) (+ P X))) (list 3)) ()) + ) + "} + .to_string(); + let res = run_string(&prog, &"(1)".to_string()).unwrap(); + assert_eq!(res.to_string(), "4"); +} + +#[test] +fn test_lambda_as_argument_to_macro() { + let prog = indoc! {" + (mod (P) + (defun map-f (A L) + (if L (c (a (f L) A) (map-f A (r L))) ()) + ) + (let ((Fs (list (lambda (X) (- X 1)) (lambda (X) (+ X 1)) (lambda (X) (* 2 X)))) + (args (list P))) + (map-f args Fs) + ) + ) + "} + .to_string(); + let res = run_string(&prog, &"(10)".to_string()).unwrap(); + assert_eq!(res.to_string(), "(9 11 20)"); +} + +#[test] +fn test_lambda_as_argument_to_macro_with_inner_let() { + let prog = indoc! {" + (mod (P) + (defun map-f (A L) + (if L (c (a (f L) A) (map-f A (r L))) ()) + ) + (let ((Fs (list (lambda (X) (let ((N (* X 3))) N)) (lambda (X) (+ X 1)) (lambda (X) (* 2 X)))) + (args (list P))) + (map-f args Fs) + ) + ) + "} + .to_string(); + let res = run_string(&prog, &"(10)".to_string()).unwrap(); + assert_eq!(res.to_string(), "(30 11 20)"); +} + +#[test] +fn test_treat_function_name_as_value() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + (defun G (X) (* 2 X)) + (defun F (X) (G (+ 1 X))) + (a F (list X)) +) + "} + .to_string(); + let res = run_string(&prog, &"(99)".to_string()).expect("should compile"); + assert_eq!(res.to_string(), "200"); +} + +#[test] +fn test_treat_function_name_as_value_filter() { + let prog = indoc! {" + (mod L + (include *standard-cl-21*) + (defun greater-than-3 (X) (> X 3)) + (defun filter (F L) (let ((rest (filter F (r L)))) (if L (if (a F (list (f L))) (c (f L) rest) rest) ()))) + (filter greater-than-3 L) + ) + "} + .to_string(); + let res = run_string(&prog, &"(1 2 3 4 5)".to_string()).expect("should compile"); + assert_eq!(res.to_string(), "(4 5)"); +} + #[test] fn test_inline_in_assign_not_actually_recursive() { let prog = indoc! {" @@ -1526,6 +1913,36 @@ fn test_simple_rest_call_inline() { assert_eq!(res.to_string(), "768"); } +#[test] +fn test_simple_rest_lambda() { + let prog = indoc! {" +(mod (Z X) + (include *standard-cl-21*) + + (defun silly-lambda-consumer (Q F) (a F (list Q))) + + (silly-lambda-consumer &rest (list X (lambda ((& Z) X) (* Z X)))) + )"} + .to_string(); + let res = run_string(&prog, &"(13 51)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "663"); +} + +#[test] +fn test_lambda_in_lambda() { + let prog = indoc! {" +(mod (Z X) + (include *standard-cl-21*) + + (defun silly-lambda-consumer (Q F) (a F (list Q))) + + (a (silly-lambda-consumer X (lambda ((& Z) X) (lambda ((& Z X)) (* Z X)))) ()) + )"} + .to_string(); + let res = run_string(&prog, &"(13 51)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "663"); +} + #[test] fn test_let_in_rest_0() { let prog = indoc! {" @@ -1556,6 +1973,243 @@ fn test_let_in_rest_1() { assert_eq!(res.to_string(), "108"); } +#[test] +fn test_lambda_override_name_arg_let_capture() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) + (lambda ((& overridden) z) (+ overridden z)) + ) + ) + + (a (F X) (list 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "50"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_let_in_lambda_1() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) + (lambda ((& overridden) z) + (let + ((z (+ 123 z))) + (+ overridden z) + ) + ) + ) + ) + + (a (F X) (list 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "173"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_let_in_lambda_2() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) + (lambda ((& overridden) z) + (let + ((overridden (+ 123 overridden))) + (+ overridden z) + ) + ) + ) + ) + + (a (F X) (list 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "173"); +} + +#[test] +fn test_lambda_override_name_arg_assign_with_assign_in_lambda_1() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (Z) + (assign overridden (* 3 Z) + (lambda ((& overridden) z) + (let + ((z (+ 123 z))) + (+ overridden z) + ) + ) + ) + ) + + (a (F X) (list 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "173"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_assign_in_lambda_1() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) ;; overridden = 33 + (lambda ((& overridden) z) ;; overridden = 33 + (assign overridden (+ 123 z) ;; overridden = 17 + 123 = 140 + (+ overridden z) ;; 157 + ) + ) + ) + ) + + (a (F X) (list 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "157"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_let_in_lambda_3() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) ;; overridden = 33 + (lambda ((& overridden) z) ;; overridden = 33 + (let ((overridden (+ 123 z))) ;; overridden = 17 + 123 = 140 + (+ overridden z) ;; 157 + ) + ) + ) + ) + + (a (F X) (list 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "157"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_assign_twice_in_lambda() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) ;; overridden = 33 + (lambda ((& overridden) y z) ;; overridden = 33 + (assign + overridden (+ 123 z) ;; overridden = 123 + 17 = 140 + y (+ 191 z overridden) ;; y = 191 + 17 + 140 = 348 + (+ overridden z y) ;; 505 + ) + ) + ) + ) + + (a (F X) (list 13 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "505"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_let_twice_in_lambda() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) ;; overridden = 33 + (lambda ((& overridden) y z) ;; overridden = 33 + (let + ((overridden (+ 123 z))) ;; overridden = 123 + 17 = 140 + (let ((y (+ 191 z overridden))) ;; y = 191 + 17 + 140 = 348 + (+ overridden z y) ;; 505 + ) + ) + ) + ) + ) + + (a (F X) (list 13 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "505"); +} + +#[test] +fn test_lambda_override_name_arg_let_with_let_star_twice_in_lambda() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) ;; overridden = 33 + (lambda ((& overridden) y z) ;; overridden = 33 + (let* + ((overridden (+ 123 z)) ;; overridden = 123 + 17 = 140 + (y (+ 191 z overridden))) ;; y = 191 + 17 + 140 = 348 + (+ overridden z y) ;; 505 + ) + ) + ) + ) + + (a (F X) (list 13 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "505"); +} + +#[test] +fn test_lambda_let_override_in_binding() { + let prog = indoc! {" +(mod (X) + (include *standard-cl-21*) + + (defun F (overridden) + (let ((overridden (* 3 overridden))) ;; overridden = 33 + (lambda ((& overridden) y z) ;; overridden = 33 + (let + ((y (+ 191 z (let ((overridden (+ 123 z))) overridden)))) ;; overridden = 123 + 17 = 140, y = 191 + 17 + 140 = 348 + (+ overridden z y) ;; 33 + 17 + 348 = 398 + ) + ) + ) + ) + + (a (F X) (list 13 17)) + )"} + .to_string(); + let res = run_string(&prog, &"(11)".to_string()).expect("should compile and run"); + assert_eq!(res.to_string(), "398"); +} + #[test] fn test_rename_in_compileform_run() { let prog = indoc! {" diff --git a/src/tests/compiler/evaluate.rs b/src/tests/compiler/evaluate.rs index a76033657..df42a5fa0 100644 --- a/src/tests/compiler/evaluate.rs +++ b/src/tests/compiler/evaluate.rs @@ -14,12 +14,14 @@ use crate::compiler::srcloc::Srcloc; use crate::classic::clvm_tools::stages::stage_0::DefaultProgramRunner; use crate::util::ErrInto; +use crate::tests::compiler::compiler::squash_name_differences; + fn shrink_expr_from_string(s: String) -> Result { let mut allocator = Allocator::new(); let runner = Rc::new(DefaultProgramRunner::new()); let opts = Rc::new(DefaultCompilerOpts::new(&"*program*".to_string())); let loc = Srcloc::start(&"*program*".to_string()); - parse_sexp(loc.clone(), s.bytes()) + let result = parse_sexp(loc.clone(), s.bytes()) .err_into() .and_then(|parsed_program| { return frontend(opts.clone(), &parsed_program); @@ -34,8 +36,11 @@ fn shrink_expr_from_string(s: String) -> Result { false, Some(EVAL_STACK_LIMIT), ); - }) - .map(|result| result.to_sexp().to_string()) + })?; + + let result_sexp = + squash_name_differences(result.to_sexp()).map_err(|e| CompileErr(loc.clone(), e))?; + Ok(result_sexp.to_string()) } #[test] @@ -119,6 +124,14 @@ fn test_simple_fe_opt_compile_1() { ); } +#[test] +fn test_lambda_eval_1() { + assert_eq!( + shrink_expr_from_string("(lambda (X) (+ X 1))".to_string()).unwrap(), + "(lambda (X_$_A) (+ X_$_A 1))".to_string() + ); +} + #[test] fn test_assign_simple_form_0() { assert_eq!( @@ -127,6 +140,14 @@ fn test_assign_simple_form_0() { ); } +#[test] +fn test_lambda_eval_2() { + assert_eq!( + shrink_expr_from_string("(a (lambda (X) (+ X 1)) (list 3))".to_string()).unwrap(), + "(q . 4)".to_string() + ); +} + #[test] fn test_assign_simple_form_1() { assert_eq!( @@ -135,6 +156,28 @@ fn test_assign_simple_form_1() { ); } +#[test] +fn test_lambda_eval_3() { + assert_eq!( + shrink_expr_from_string( + "(let ((L 10)) (a (lambda ((& L) X) (+ X L)) (list 3)))".to_string() + ) + .unwrap(), + "(q . 13)".to_string() + ); +} + +#[test] +fn test_lambda_eval_4() { + assert_eq!( + shrink_expr_from_string( + "(a (let ((L 10)) (lambda ((& L) X) (+ X L))) (list 3))".to_string() + ) + .unwrap(), + "(q . 13)".to_string() + ); +} + #[test] fn test_assign_simple_form_2() { assert_eq!( diff --git a/src/tests/compiler/repl.rs b/src/tests/compiler/repl.rs index a392c77b8..d5c0977ca 100644 --- a/src/tests/compiler/repl.rs +++ b/src/tests/compiler/repl.rs @@ -302,6 +302,95 @@ fn test_eval_list_partially_evaluated_xyz() { ); } +#[test] +fn test_defun_value_in_repl_0() { + assert_eq!( + test_repl_outcome(vec![ + "(defun greater-than-3 (X) (> X 3))", + "(a greater-than-3 (list 5))" + ]) + .unwrap() + .unwrap(), + "(q . 1)" + ); +} + +#[test] +fn test_defun_value_repl_map() { + assert_eq!( + test_repl_outcome(vec![ + "(defun greater-than-3 (X) (> X 3))", + "(defun map (F L) (if L (c (a F (list (f L))) (map F (r L))) ()))", + "(map greater-than-3 (list 1 2 3 4 5))" + ]) + .unwrap() + .unwrap(), + "(q () () () 1 1)" + ); +} + +#[test] +fn test_lambda_eval_5() { + assert_eq!( + test_repl_outcome_with_stack_limit( + vec![ + "(defun GetFun (L) (lambda ((& L) X) (+ X L)))", + "(a (GetFun 10) (list 3))" + ], + None + ) + .unwrap() + .unwrap(), + "(q . 13)" + ); +} + +#[test] +fn test_lambda_eval_6() { + assert_eq!( + test_repl_outcome(vec![ + indoc! {" +(defun visit-atoms (fn acc mask path pattern) + (if (l pattern) + (visit-atoms + fn + (visit-atoms fn acc (* 2 mask) path (f pattern)) + (* 2 mask) + (logior mask path) + (r pattern) + ) + + (a fn (list acc (logior path mask) pattern)) + ) + ) +"}, + indoc! {" + (defun if-match (match) + (c 1 + (visit-atoms + (lambda (cb path pat) + (if (l pat) + (list cb \"A\") ;; Unbound use of cb + (list cb \"B\") + ) + ) + () + 1 + 0 + match + ) + ) + ) + +"}, + "(if-match (q test \"test\" t1 (t2 . t3)))" + ]) + .unwrap() + .unwrap(), + "(q 1 (((((() B) B) B) B) B) B)" + ); +} + #[test] fn test_eval_new_bls_operator() { assert_eq!( @@ -315,3 +404,68 @@ fn test_eval_new_bls_operator() { "(q)" ); } + +#[test] +fn test_repl_base_lambda_case() { + assert_eq!( + test_repl_outcome_with_stack_limit( + vec![ + "(defun F (X F) (a F (list X)))".to_string(), + "(F 3 (lambda (Y) (+ Y 9)))".to_string(), + ], + None + ) + .unwrap() + .unwrap(), + "(q . 12)" + ); +} + +#[test] +fn test_repl_rest_lambda_case() { + assert_eq!( + test_repl_outcome_with_stack_limit( + vec![ + "(defun F (X F) (a F (list X)))".to_string(), + "(F &rest (list 3 (lambda (Y) (+ Y 9))))".to_string() + ], + None + ) + .unwrap() + .unwrap(), + "(q . 12)" + ); +} + +#[test] +fn test_repl_lambda_with_captures_rest() { + assert_eq!( + test_repl_outcome_with_stack_limit( + vec![ + "(defun map (F L) (if L (c (a F (list (f L))) (map F (r L))) ()))".to_string(), + "(defun F (X L) (map &rest (list (lambda ((& X) Y) (+ X Y)) L)))".to_string(), + "(F 3 (list 99 101 103))".to_string() + ], + None + ) + .unwrap() + .unwrap(), + "(q 102 104 106)" + ); +} + +#[test] +fn test_repl_lambda_with_captures_out_of_own_function() { + assert_eq!( + test_repl_outcome_with_stack_limit( + vec![ + "(defun F (X) (lambda ((& X) Y) (+ X Y)))".to_string(), + "(a (F 3) (list 4))".to_string(), + ], + None + ) + .unwrap() + .unwrap(), + "(q . 7)" + ); +} diff --git a/support/test-game-referee.sh b/support/test-game-referee.sh new file mode 100755 index 000000000..31e28a93a --- /dev/null +++ b/support/test-game-referee.sh @@ -0,0 +1,16 @@ +#!/bin/bash -x + +if [ "x$1" = x ] ; then + echo "usage: test-game-referee.sh [game-referee-version]" + exit 1 +fi + +REF_SUBDIR="$1" + +python -m pip install --upgrade pip +python -m pip install chia_rs==0.2.5 +python -m pip install clvm_tools +python -m pip install pytest + +export PYTHONPATH=$PWD/resources/tests:.:$PYTHONPATH +(cd "${REF_SUBDIR}" && pytest -s .) diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 23466a11e..63de4da73 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -117,7 +117,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clvm_tools_rs" -version = "0.1.35" +version = "0.1.36" dependencies = [ "binascii", "bls12_381", @@ -148,7 +148,7 @@ dependencies = [ [[package]] name = "clvm_tools_wasm" -version = "0.1.35" +version = "0.1.36" dependencies = [ "clvm_tools_rs", "clvmr", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "clvmr" -version = "0.2.7" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2890f01537f1be43d2767ae71bbba0d0b3543dbb1ee892092d0ed4d913227fc" +checksum = "9cd344b6dc76235f446025fe9ebe54aa6131e2e59acb49e16be48a3bb3492491" dependencies = [ "bls12_381", "getrandom", diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index fda4860f0..b37b03e44 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clvm_tools_wasm" -version = "0.1.35" +version = "0.1.36" edition = "2018" authors = ["Art Yerkes "] description = "tools for working with chialisp language; compiler, repl, python and wasm bindings" @@ -23,4 +23,4 @@ wasm-bindgen = "=0.2.83" wasm-bindgen-test = "=0.3.25" js-sys = "0.3.60" num-bigint = "0.4.0" -num-traits = "0.2.15" \ No newline at end of file +num-traits = "0.2.15"