Compare commits
19 Commits
1daef81b35
...
9a6da002e2
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 9a6da002e2 | |
Savanni D'Gerinel | 60b0f1b736 | |
Savanni D'Gerinel | 0dd7344a09 | |
Savanni D'Gerinel | 904a866de0 | |
Savanni D'Gerinel | 17050ed4d6 | |
Savanni D'Gerinel | 97c52de2cf | |
Savanni D'Gerinel | fee2e598d3 | |
Savanni D'Gerinel | 889c3cec94 | |
Savanni D'Gerinel | 06157604b9 | |
Savanni D'Gerinel | d68e088500 | |
Savanni D'Gerinel | 27d87fcd02 | |
Savanni D'Gerinel | edcf20cc25 | |
Savanni D'Gerinel | 78af31f6f9 | |
Savanni D'Gerinel | 52e27318fe | |
Savanni D'Gerinel | e746190a4a | |
Savanni D'Gerinel | 300704297f | |
Savanni D'Gerinel | 3cd39af060 | |
Savanni D'Gerinel | 037484e7b4 | |
Savanni D'Gerinel | 6d51ae8479 |
24
Makefile
24
Makefile
|
@ -35,3 +35,27 @@ ifc-dev:
|
||||||
ifc-test:
|
ifc-test:
|
||||||
cd ifc && make test
|
cd ifc && make test
|
||||||
|
|
||||||
|
kifu-core/dev:
|
||||||
|
cd kifu/kifu-core && make test
|
||||||
|
|
||||||
|
kifu-core/test:
|
||||||
|
cd kifu/kifu-core && make test
|
||||||
|
|
||||||
|
kifu-core/test-oneshot:
|
||||||
|
cd kifu/kifu-core && make test-oneshot
|
||||||
|
|
||||||
|
kifu-gtk:
|
||||||
|
cd kifu/kifu-gtk && make release
|
||||||
|
|
||||||
|
kifu-gtk/dev:
|
||||||
|
cd kifu/kifu-gtk && make dev
|
||||||
|
|
||||||
|
kifu-pwa:
|
||||||
|
cd kifu/kifu-pwa && make release
|
||||||
|
|
||||||
|
kifu-pwa/dev:
|
||||||
|
# pushd kifu/ffi/wasm && make && popd
|
||||||
|
pushd kifu/kifu-pwa && make dev
|
||||||
|
|
||||||
|
kifu-pwa/test-server:
|
||||||
|
pushd kifu/kifu-pwa && make test-server
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
{ pkgs, typeshare }:
|
|
||||||
let
|
|
||||||
standardOverride = attrs: {
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.gtk4
|
|
||||||
];
|
|
||||||
buildInputs = [
|
|
||||||
typeshare
|
|
||||||
];
|
|
||||||
verbose = true;
|
|
||||||
};
|
|
||||||
customBuildInfo = pkgs: pkgs.buildRustCrate.override {
|
|
||||||
defaultCrateOverrides = pkgs.defaultCrateOverrides // {
|
|
||||||
cairo-sys-rs = standardOverride;
|
|
||||||
graphene-sys = standardOverride;
|
|
||||||
gobject-sys = standardOverride;
|
|
||||||
pango-sys = standardOverride;
|
|
||||||
gio-sys = standardOverride;
|
|
||||||
gdk-pixbuf-sys = standardOverride;
|
|
||||||
gdk4-sys = standardOverride;
|
|
||||||
gsk4-sys = standardOverride;
|
|
||||||
gtk4-sys = standardOverride;
|
|
||||||
kifu-gtk = attrs: {
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.glib
|
|
||||||
typeshare
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in (import ./Cargo.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
buildRustCrateForPkgs = customBuildInfo;
|
|
||||||
release = true;
|
|
||||||
}).rootCrate.build
|
|
84
flake.lock
84
flake.lock
|
@ -49,24 +49,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_3": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681202837,
|
|
||||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681932375,
|
"lastModified": 1681932375,
|
||||||
|
@ -99,22 +81,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681358109,
|
|
||||||
"narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_4": {
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681303793,
|
"lastModified": 1681303793,
|
||||||
"narHash": "sha256-JEdQHsYuCfRL2PICHlOiH/2ue3DwoxUX7DJ6zZxZXFk=",
|
"narHash": "sha256-JEdQHsYuCfRL2PICHlOiH/2ue3DwoxUX7DJ6zZxZXFk=",
|
||||||
|
@ -154,7 +120,6 @@
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pkgs-cargo2nix": "pkgs-cargo2nix",
|
"pkgs-cargo2nix": "pkgs-cargo2nix",
|
||||||
"rust-overlay": "rust-overlay_2",
|
|
||||||
"typeshare": "typeshare",
|
"typeshare": "typeshare",
|
||||||
"unstable": "unstable"
|
"unstable": "unstable"
|
||||||
}
|
}
|
||||||
|
@ -184,25 +149,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-overlay_2": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils_2",
|
|
||||||
"nixpkgs": "nixpkgs_3"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1683080331,
|
|
||||||
"narHash": "sha256-nGDvJ1DAxZIwdn6ww8IFwzoHb2rqBP4wv/65Wt5vflk=",
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"rev": "d59c3fa0cba8336e115b376c2d9e91053aa59e56",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
|
@ -218,37 +164,21 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"typeshare": {
|
"typeshare": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_3",
|
"flake-utils": "flake-utils_2",
|
||||||
"nixpkgs": "nixpkgs_4"
|
"nixpkgs": "nixpkgs_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681765787,
|
"lastModified": 1683230849,
|
||||||
"narHash": "sha256-ZzS4jSXeqhPfhxgnhar2EjcvSVU7nwsraTzuOn2cFtY=",
|
"narHash": "sha256-PjIKxX1xIALyWD8NyDeoIZMMfsS4/w/AweAcYOcsLNs=",
|
||||||
"owner": "savannidgerinel",
|
"owner": "1Password",
|
||||||
"repo": "typeshare",
|
"repo": "typeshare",
|
||||||
"rev": "e2079972f167b2891063dc20868cecc594bf9aa1",
|
"rev": "2687f8d86ef38c07819715a2f31a21ffc25504e4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "savannidgerinel",
|
"owner": "1Password",
|
||||||
"ref": "savannidgerinel/add-a-nix-flake",
|
|
||||||
"repo": "typeshare",
|
"repo": "typeshare",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
79
flake.nix
79
flake.nix
|
@ -5,11 +5,10 @@
|
||||||
nixpkgs.url = "nixpkgs/nixos-22.11";
|
nixpkgs.url = "nixpkgs/nixos-22.11";
|
||||||
unstable.url = "nixpkgs/nixos-unstable";
|
unstable.url = "nixpkgs/nixos-unstable";
|
||||||
pkgs-cargo2nix.url = "github:cargo2nix/cargo2nix";
|
pkgs-cargo2nix.url = "github:cargo2nix/cargo2nix";
|
||||||
typeshare.url = "github:savannidgerinel/typeshare/savannidgerinel/add-a-nix-flake";
|
typeshare.url = "github:1Password/typeshare";
|
||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, unstable, pkgs-cargo2nix, typeshare, rust-overlay, ... }:
|
outputs = { self, nixpkgs, unstable, pkgs-cargo2nix, typeshare, ... }:
|
||||||
let
|
let
|
||||||
version = builtins.string 0 8 self.lastModifiedDate;
|
version = builtins.string 0 8 self.lastModifiedDate;
|
||||||
supportedSystems = [ "x86_64-linux" ];
|
supportedSystems = [ "x86_64-linux" ];
|
||||||
|
@ -40,66 +39,54 @@
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.sqlite
|
pkgs.sqlite
|
||||||
pkgs.rustup
|
pkgs.rustup
|
||||||
|
pkgs.cargo-nextest
|
||||||
pkgs.crate2nix
|
pkgs.crate2nix
|
||||||
pkgs.wasm-pack
|
pkgs.wasm-pack
|
||||||
pkgs.node2nix
|
typeshare.packages."x86_64-linux".default
|
||||||
pkgs.wasm-bindgen-cli
|
|
||||||
pkgs-unstable.typeshare
|
|
||||||
];
|
];
|
||||||
LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
|
LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
};
|
};
|
||||||
packages."x86_64-linux" =
|
packages."x86_64-linux" =
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
system = "x86_64-linux";
|
|
||||||
overlays = [ (import rust-overlay) ];
|
|
||||||
};
|
|
||||||
standardOverride = attrs: {
|
standardOverride = attrs: {
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.gtk4
|
pkgs.gtk4
|
||||||
];
|
];
|
||||||
buildInputs = [
|
|
||||||
typeshare.packages."x86_64-linux".default
|
|
||||||
];
|
|
||||||
verbose = true;
|
verbose = true;
|
||||||
};
|
};
|
||||||
|
customBuildInfo = pkgs: pkgs.buildRustCrate.override {
|
||||||
rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain;
|
defaultCrateOverrides = pkgs.defaultCrateOverrides // {
|
||||||
|
cairo-sys-rs = standardOverride;
|
||||||
|
graphene-sys = standardOverride;
|
||||||
|
gobject-sys = standardOverride;
|
||||||
|
pango-sys = standardOverride;
|
||||||
|
gio-sys = standardOverride;
|
||||||
|
gdk-pixbuf-sys = standardOverride;
|
||||||
|
gdk4-sys = standardOverride;
|
||||||
|
gsk4-sys = standardOverride;
|
||||||
|
gtk4-sys = standardOverride;
|
||||||
|
kifu-gtk = attrs: {
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.glib
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
in {
|
in {
|
||||||
kifu-gtk = import ./kifu/kifu-gtk {
|
# gobject-sys = pkgs.buildRustCrate cargo.internal.crates.gobject-sys;
|
||||||
|
kifu-gtk = (import ./kifu/kifu-gtk/Cargo.nix {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
typeshare = typeshare.packages."x86_64-linux".default;
|
buildRustCrateForPkgs = customBuildInfo;
|
||||||
};
|
rootFeatures = [ "screenplay" ];
|
||||||
|
release = true;
|
||||||
kifu-wasm = import ./kifu/kifu-wasm {
|
}).rootCrate.build;
|
||||||
|
cyberpunk-splash = (import ./cyberpunk-splash/Cargo.nix {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
typeshare = typeshare.packages."x86_64-linux".default;
|
buildRustCrateForPkgs = customBuildInfo;
|
||||||
wasm-pack = pkgs.wasm-pack;
|
release = true;
|
||||||
rustc = rust.availableComponents.rustc;
|
}).rootCrate.build;
|
||||||
};
|
|
||||||
|
|
||||||
kifu-pwa = let
|
|
||||||
deps = (import ./kifu/kifu-pwa { inherit pkgs; }).nodeDependencies;
|
|
||||||
in pkgs.stdenv.mkDerivation {
|
|
||||||
name = "kifu-pwa";
|
|
||||||
src = ./kifu/kifu-pwa;
|
|
||||||
|
|
||||||
buildInputs = [ pkgs.nodejs pkgs.nodePackages.webpack-cli ];
|
|
||||||
buildPhase = ''
|
|
||||||
ln -s ${deps}/lib/node_modules ./node_modules
|
|
||||||
export PATH="${deps}/bin:$PATH"
|
|
||||||
webpack
|
|
||||||
'';
|
|
||||||
installPhase = ''
|
|
||||||
cp -r dist $out
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
cyberpunk-splash = import ./cyberpunk-splash {
|
|
||||||
inherit pkgs;
|
|
||||||
typeshare = typeshare.packages."x86_64-linux".default;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
{ mkDerivation
|
|
||||||
, rustPlatform
|
|
||||||
, typeshare
|
|
||||||
, wasm-pack
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
in mkDerivation {
|
|
||||||
name = "kifu-wasm";
|
|
||||||
version = "0.0.0";
|
|
||||||
|
|
||||||
src = ./.;
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
pwd
|
|
||||||
exit 1
|
|
||||||
'';
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kifu-core = { path = "../kifu-core" }
|
kifu-core = { path = "../../kifu-core" }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
wasm-bindgen-futures = "*"
|
wasm-bindgen-futures = "*"
|
||||||
|
|
|
@ -19,15 +19,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.12.0"
|
version = "3.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytes"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
|
@ -95,7 +89,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"scratch",
|
"scratch",
|
||||||
"syn 2.0.15",
|
"syn 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -112,7 +106,16 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.15",
|
"syn 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grid"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0634107a3a005070dd73e27e74ecb691a94e9e5ba7829f434db7fbf73a6b5c47"
|
||||||
|
dependencies = [
|
||||||
|
"no-std-compat",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -158,16 +161,17 @@ dependencies = [
|
||||||
name = "kifu-core"
|
name = "kifu-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"grid",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"thiserror",
|
||||||
"typeshare",
|
"typeshare",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.142"
|
version = "0.2.140"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
|
@ -188,10 +192,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "no-std-compat"
|
||||||
version = "2.5.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
|
@ -218,17 +222,11 @@ version = "1.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.56"
|
version = "1.0.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -256,22 +254,22 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.160"
|
version = "1.0.162"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.160"
|
version = "1.0.162"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.15",
|
"syn 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -298,9 +296,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.15"
|
version = "2.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -317,28 +315,23 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "thiserror"
|
||||||
version = "1.26.0"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
|
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"thiserror-impl",
|
||||||
"bytes",
|
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio-macros",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "thiserror-impl"
|
||||||
version = "1.8.2"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -466,31 +459,7 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets 0.48.0",
|
"windows-targets",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.45.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.42.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.42.2",
|
|
||||||
"windows_aarch64_msvc 0.42.2",
|
|
||||||
"windows_i686_gnu 0.42.2",
|
|
||||||
"windows_i686_msvc 0.42.2",
|
|
||||||
"windows_x86_64_gnu 0.42.2",
|
|
||||||
"windows_x86_64_gnullvm 0.42.2",
|
|
||||||
"windows_x86_64_msvc 0.42.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -499,93 +468,51 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm 0.48.0",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc 0.48.0",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu 0.48.0",
|
"windows_i686_gnu",
|
||||||
"windows_i686_msvc 0.48.0",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu 0.48.0",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm 0.48.0",
|
"windows_x86_64_gnullvm",
|
||||||
"windows_x86_64_msvc 0.48.0",
|
"windows_x86_64_msvc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.26", features = [ "sync", "io-util", "macros", "rt", "time" ] }
|
grid = { version = "0.9" }
|
||||||
typeshare = { version = "1" }
|
|
||||||
serde = { version = "1", features = [ "derive" ] }
|
serde = { version = "1", features = [ "derive" ] }
|
||||||
|
thiserror = { version = "1" }
|
||||||
|
typeshare = { version = "1" }
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo watch -x 'nextest run'
|
||||||
|
|
||||||
|
test-oneshot:
|
||||||
|
cargo nextest run
|
|
@ -3,9 +3,9 @@ use std::process::Command;
|
||||||
fn main() {
|
fn main() {
|
||||||
Command::new("typeshare")
|
Command::new("typeshare")
|
||||||
.args(&[
|
.args(&[
|
||||||
"../../kifu-core",
|
".",
|
||||||
"--lang=typescript",
|
"--lang=typescript",
|
||||||
"--output-file=pkg/core.d.ts",
|
"--output-file=typeshare/core.d.ts",
|
||||||
])
|
])
|
||||||
.status()
|
.status()
|
||||||
.unwrap();
|
.unwrap();
|
|
@ -15,8 +15,8 @@ pub enum Request {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct PlayStoneRequest {
|
pub struct PlayStoneRequest {
|
||||||
pub column: u8,
|
pub column: usize,
|
||||||
pub row: u8,
|
pub row: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -0,0 +1,633 @@
|
||||||
|
use crate::{BoardError, Color, Size};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Board {
|
||||||
|
pub size: Size,
|
||||||
|
pub groups: Vec<Group>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Board {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
write!(f, " ")?;
|
||||||
|
// for c in 'A'..'U' {
|
||||||
|
for c in 0..19 {
|
||||||
|
write!(f, "{:2}", c)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
|
||||||
|
for row in 0..self.size.height {
|
||||||
|
write!(f, " {:2}", row)?;
|
||||||
|
for column in 0..self.size.width {
|
||||||
|
match self.stone(&Coordinate { column, row }) {
|
||||||
|
None => write!(f, " .")?,
|
||||||
|
Some(Color::Black) => write!(f, " X")?,
|
||||||
|
Some(Color::White) => write!(f, " O")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Board {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.size != other.size {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for group in self.groups.iter() {
|
||||||
|
if !other.groups.contains(&group) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for group in other.groups.iter() {
|
||||||
|
if !self.groups.contains(&group) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
size: Size {
|
||||||
|
width: 19,
|
||||||
|
height: 19,
|
||||||
|
},
|
||||||
|
groups: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_coordinates(
|
||||||
|
mut coordinates: impl Iterator<Item = (Coordinate, Color)>,
|
||||||
|
) -> Result<Self, BoardError> {
|
||||||
|
coordinates.try_fold(Self::new(), |board, (coordinate, color)| {
|
||||||
|
board.place_stone(coordinate, color)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
|
||||||
|
pub struct Coordinate {
|
||||||
|
pub column: usize,
|
||||||
|
pub row: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
pub fn place_stone(mut self, coordinate: Coordinate, color: Color) -> Result<Self, BoardError> {
|
||||||
|
if let Some(_) = self.stone(&coordinate) {
|
||||||
|
return Err(BoardError::InvalidPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut friendly_group = self
|
||||||
|
.adjacencies(&coordinate)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|c| self.stone(c) == Some(color))
|
||||||
|
.filter_map(|c| self.group(&c).map(|g| g.coordinates.clone()))
|
||||||
|
.fold(HashSet::new(), |acc, set| {
|
||||||
|
acc.union(&set).cloned().collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
friendly_group.insert(coordinate.clone());
|
||||||
|
|
||||||
|
self.groups
|
||||||
|
.retain(|g| g.coordinates.is_disjoint(&friendly_group));
|
||||||
|
let friendly_group = Group {
|
||||||
|
color,
|
||||||
|
coordinates: friendly_group,
|
||||||
|
};
|
||||||
|
self.groups.push(friendly_group.clone());
|
||||||
|
|
||||||
|
let adjacent_groups = self.adjacent_groups(&friendly_group);
|
||||||
|
for group in adjacent_groups {
|
||||||
|
if self.liberties(&group) == 0 {
|
||||||
|
self.remove_group(&group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.liberties(&friendly_group) == 0 {
|
||||||
|
return Err(BoardError::SelfCapture);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stone(&self, coordinate: &Coordinate) -> Option<Color> {
|
||||||
|
self.groups
|
||||||
|
.iter()
|
||||||
|
.find(|g| g.contains(coordinate))
|
||||||
|
.map(|g| g.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn group(&self, coordinate: &Coordinate) -> Option<&Group> {
|
||||||
|
self.groups
|
||||||
|
.iter()
|
||||||
|
.find(|g| g.coordinates.contains(coordinate))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_group(&mut self, group: &Group) {
|
||||||
|
self.groups.retain(|g| g != group);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjacent_groups(&self, group: &Group) -> Vec<Group> {
|
||||||
|
let adjacent_spaces = self.group_halo(group).into_iter();
|
||||||
|
let mut grps: Vec<Group> = Vec::new();
|
||||||
|
|
||||||
|
adjacent_spaces.for_each(|coord| match self.group(&coord) {
|
||||||
|
None => return,
|
||||||
|
Some(adj) => {
|
||||||
|
if group.color == adj.color {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if grps.iter().any(|g| g.coordinates.contains(&coord)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
grps.push(adj.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn group_halo(&self, group: &Group) -> HashSet<Coordinate> {
|
||||||
|
group
|
||||||
|
.coordinates
|
||||||
|
.iter()
|
||||||
|
.map(|c| self.adjacencies(c))
|
||||||
|
.flatten()
|
||||||
|
.collect::<HashSet<Coordinate>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn liberties(&self, group: &Group) -> usize {
|
||||||
|
self.group_halo(group)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|c| self.stone(&c) == None)
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjacencies(&self, coordinate: &Coordinate) -> Vec<Coordinate> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
if coordinate.column > 0 {
|
||||||
|
v.push(Coordinate {
|
||||||
|
column: coordinate.column - 1,
|
||||||
|
row: coordinate.row,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if coordinate.row > 0 {
|
||||||
|
v.push(Coordinate {
|
||||||
|
column: coordinate.column,
|
||||||
|
row: coordinate.row - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
v.push(Coordinate {
|
||||||
|
column: coordinate.column + 1,
|
||||||
|
row: coordinate.row,
|
||||||
|
});
|
||||||
|
v.push(Coordinate {
|
||||||
|
column: coordinate.column,
|
||||||
|
row: coordinate.row + 1,
|
||||||
|
});
|
||||||
|
v.into_iter().filter(|c| self.within_board(c)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn within_board(&self, coordinate: &Coordinate) -> bool {
|
||||||
|
coordinate.column < self.size.width && coordinate.row < self.size.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Group {
|
||||||
|
color: Color,
|
||||||
|
coordinates: HashSet<Coordinate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Group {
|
||||||
|
fn contains(&self, coordinate: &Coordinate) -> bool {
|
||||||
|
self.coordinates.contains(coordinate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/* Two players (Black and White) take turns and Black plays first
|
||||||
|
* Stones are placed on the line intersections and not moved.
|
||||||
|
* A stone with no liberties is removed from the board.
|
||||||
|
* A group of stones of the same color share liberties.
|
||||||
|
* A stone at the edge of the board has only three liberties.
|
||||||
|
* A stone at the corner of the board has only two liberties.
|
||||||
|
* A stone may not be placed in a suicidal position.
|
||||||
|
* A stone placed in a suicidal position is legal if it captures other stones first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn with_example_board(test: impl FnOnce(Board)) {
|
||||||
|
let board = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 3, row: 3 }, Color::White),
|
||||||
|
(Coordinate { column: 3, row: 4 }, Color::White),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 8, row: 3 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 3 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 4 }, Color::Black),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 15, row: 3 }, Color::White),
|
||||||
|
(Coordinate { column: 15, row: 4 }, Color::White),
|
||||||
|
(Coordinate { column: 15, row: 5 }, Color::White),
|
||||||
|
(Coordinate { column: 14, row: 4 }, Color::White),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 3, row: 8 }, Color::White),
|
||||||
|
(Coordinate { column: 3, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 4, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 3, row: 10 }, Color::Black),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 0, row: 0 }, Color::White),
|
||||||
|
(Coordinate { column: 1, row: 0 }, Color::White),
|
||||||
|
(Coordinate { column: 0, row: 1 }, Color::White),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 9, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 9 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 8 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 10 }, Color::Black),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 0, row: 17 }, Color::White),
|
||||||
|
(Coordinate { column: 1, row: 18 }, Color::White),
|
||||||
|
(Coordinate { column: 0, row: 18 }, Color::White),
|
||||||
|
(Coordinate { column: 0, row: 16 }, Color::Black),
|
||||||
|
(Coordinate { column: 1, row: 17 }, Color::Black),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 4, row: 17 }, Color::Black),
|
||||||
|
(Coordinate { column: 5, row: 17 }, Color::Black),
|
||||||
|
(Coordinate { column: 6, row: 17 }, Color::Black),
|
||||||
|
(Coordinate { column: 4, row: 18 }, Color::Black),
|
||||||
|
(Coordinate { column: 6, row: 18 }, Color::Black),
|
||||||
|
(Coordinate { column: 3, row: 17 }, Color::White),
|
||||||
|
(Coordinate { column: 3, row: 18 }, Color::White),
|
||||||
|
(Coordinate { column: 4, row: 16 }, Color::White),
|
||||||
|
(Coordinate { column: 5, row: 16 }, Color::White),
|
||||||
|
(Coordinate { column: 6, row: 16 }, Color::White),
|
||||||
|
(Coordinate { column: 7, row: 17 }, Color::White),
|
||||||
|
(Coordinate { column: 7, row: 18 }, Color::White),
|
||||||
|
/* */
|
||||||
|
(Coordinate { column: 17, row: 0 }, Color::White),
|
||||||
|
(Coordinate { column: 17, row: 1 }, Color::White),
|
||||||
|
(Coordinate { column: 18, row: 1 }, Color::White),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
test(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_gets_adjacencies_for_coordinate() {
|
||||||
|
let board = Board::new();
|
||||||
|
for column in 0..19 {
|
||||||
|
for row in 0..19 {
|
||||||
|
for coordinate in board.adjacencies(&Coordinate { column, row }) {
|
||||||
|
assert!(
|
||||||
|
board.within_board(&coordinate),
|
||||||
|
"{} {}: {:?}",
|
||||||
|
column,
|
||||||
|
row,
|
||||||
|
coordinate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_counts_individual_liberties() {
|
||||||
|
let board = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 3, row: 3 }, Color::White),
|
||||||
|
(Coordinate { column: 0, row: 3 }, Color::White),
|
||||||
|
(Coordinate { column: 0, row: 0 }, Color::White),
|
||||||
|
(Coordinate { column: 18, row: 9 }, Color::Black),
|
||||||
|
(
|
||||||
|
Coordinate {
|
||||||
|
column: 18,
|
||||||
|
row: 18,
|
||||||
|
},
|
||||||
|
Color::Black,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(board.group(&Coordinate { column: 18, row: 3 }).is_none());
|
||||||
|
assert_eq!(
|
||||||
|
board
|
||||||
|
.group(&Coordinate { column: 3, row: 3 })
|
||||||
|
.map(|g| board.liberties(&g)),
|
||||||
|
Some(4)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
board
|
||||||
|
.group(&Coordinate { column: 0, row: 3 })
|
||||||
|
.map(|g| board.liberties(&g)),
|
||||||
|
Some(3)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
board
|
||||||
|
.group(&Coordinate { column: 0, row: 0 })
|
||||||
|
.map(|g| board.liberties(&g)),
|
||||||
|
Some(2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
board
|
||||||
|
.group(&Coordinate { column: 18, row: 9 })
|
||||||
|
.map(|g| board.liberties(&g)),
|
||||||
|
Some(3)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
board
|
||||||
|
.group(&Coordinate {
|
||||||
|
column: 18,
|
||||||
|
row: 18
|
||||||
|
})
|
||||||
|
.map(|g| board.liberties(&g)),
|
||||||
|
Some(2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stones_share_liberties() {
|
||||||
|
with_example_board(|board: Board| {
|
||||||
|
let test_cases = vec![
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 0, row: 0 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 0, row: 0 },
|
||||||
|
Coordinate { column: 1, row: 0 },
|
||||||
|
Coordinate { column: 0, row: 1 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(3),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 1, row: 0 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 0, row: 0 },
|
||||||
|
Coordinate { column: 1, row: 0 },
|
||||||
|
Coordinate { column: 0, row: 1 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(3),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 9, row: 9 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![Coordinate { column: 9, row: 9 }].into_iter().collect(),
|
||||||
|
}),
|
||||||
|
Some(1),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 3, row: 4 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 3, row: 3 },
|
||||||
|
Coordinate { column: 3, row: 4 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(6),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 9, row: 3 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::Black,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 8, row: 3 },
|
||||||
|
Coordinate { column: 9, row: 3 },
|
||||||
|
Coordinate { column: 9, row: 4 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(7),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 15, row: 4 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 15, row: 3 },
|
||||||
|
Coordinate { column: 15, row: 4 },
|
||||||
|
Coordinate { column: 15, row: 5 },
|
||||||
|
Coordinate { column: 14, row: 4 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(8),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 3, row: 9 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 3, row: 8 },
|
||||||
|
Coordinate { column: 3, row: 9 },
|
||||||
|
Coordinate { column: 4, row: 9 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(6),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 0, row: 18 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 0, row: 17 },
|
||||||
|
Coordinate { column: 0, row: 18 },
|
||||||
|
Coordinate { column: 1, row: 18 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(1),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
board.clone(),
|
||||||
|
Coordinate { column: 0, row: 17 },
|
||||||
|
Some(Group {
|
||||||
|
color: Color::White,
|
||||||
|
coordinates: vec![
|
||||||
|
Coordinate { column: 0, row: 17 },
|
||||||
|
Coordinate { column: 0, row: 18 },
|
||||||
|
Coordinate { column: 1, row: 18 },
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
Some(1),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
println!("{}", board);
|
||||||
|
for (board, coordinate, group, liberties) in test_cases {
|
||||||
|
assert_eq!(board.group(&coordinate), group.as_ref());
|
||||||
|
assert_eq!(
|
||||||
|
board.group(&coordinate).map(|g| board.liberties(&g)),
|
||||||
|
liberties,
|
||||||
|
"{:?}",
|
||||||
|
coordinate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_finds_adjacent_groups() {
|
||||||
|
with_example_board(|board| {
|
||||||
|
let group = board
|
||||||
|
.group(&Coordinate { column: 0, row: 0 })
|
||||||
|
.cloned()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(board.adjacent_groups(&group), Vec::new());
|
||||||
|
|
||||||
|
let group = board
|
||||||
|
.group(&Coordinate { column: 3, row: 10 })
|
||||||
|
.cloned()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(board.adjacent_groups(&group).len(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn surrounding_a_group_removes_it() {
|
||||||
|
with_example_board(|board| {
|
||||||
|
let board = board
|
||||||
|
.place_stone(Coordinate { column: 10, row: 9 }, Color::Black)
|
||||||
|
.unwrap();
|
||||||
|
assert!(board.stone(&Coordinate { column: 9, row: 9 }).is_none());
|
||||||
|
|
||||||
|
let board = board
|
||||||
|
.place_stone(Coordinate { column: 2, row: 18 }, Color::Black)
|
||||||
|
.unwrap();
|
||||||
|
assert!(board.stone(&Coordinate { column: 0, row: 18 }).is_none());
|
||||||
|
assert!(board.stone(&Coordinate { column: 1, row: 18 }).is_none());
|
||||||
|
assert!(board.stone(&Coordinate { column: 0, row: 17 }).is_none());
|
||||||
|
assert!(board.group(&Coordinate { column: 0, row: 18 }).is_none());
|
||||||
|
|
||||||
|
let board = board
|
||||||
|
.place_stone(Coordinate { column: 5, row: 18 }, Color::White)
|
||||||
|
.unwrap();
|
||||||
|
assert!(board.stone(&Coordinate { column: 4, row: 17 }).is_none());
|
||||||
|
assert!(board.stone(&Coordinate { column: 5, row: 17 }).is_none());
|
||||||
|
assert!(board.stone(&Coordinate { column: 6, row: 17 }).is_none());
|
||||||
|
assert!(board.stone(&Coordinate { column: 4, row: 18 }).is_none());
|
||||||
|
assert!(board.stone(&Coordinate { column: 6, row: 18 }).is_none());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn self_capture_is_forbidden() {
|
||||||
|
with_example_board(|board| {
|
||||||
|
{
|
||||||
|
let board = board.clone();
|
||||||
|
let res = board.place_stone(Coordinate { column: 18, row: 0 }, Color::Black);
|
||||||
|
assert_eq!(res, Err(BoardError::SelfCapture));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let board = board.clone();
|
||||||
|
let res = board.place_stone(Coordinate { column: 5, row: 18 }, Color::Black);
|
||||||
|
assert_eq!(res, Err(BoardError::SelfCapture));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_group_comparisons() {
|
||||||
|
{
|
||||||
|
let b1 = Board::from_coordinates(
|
||||||
|
vec![(Coordinate { column: 7, row: 9 }, Color::White)].into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let b2 = Board::from_coordinates(
|
||||||
|
vec![(Coordinate { column: 7, row: 9 }, Color::White)].into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(b1, b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let b1 = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 7, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 10 }, Color::White),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let b2 = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 8, row: 10 }, Color::White),
|
||||||
|
(Coordinate { column: 7, row: 9 }, Color::White),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(b1, b2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_boards_can_be_compared() {
|
||||||
|
let board = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 7, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 8 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 10 }, Color::White),
|
||||||
|
(Coordinate { column: 9, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 10, row: 9 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 8 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 10 }, Color::Black),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let b1 = board
|
||||||
|
.clone()
|
||||||
|
.place_stone(Coordinate { column: 8, row: 9 }, Color::Black)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let b2 = b1
|
||||||
|
.clone()
|
||||||
|
.place_stone(Coordinate { column: 9, row: 9 }, Color::White)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(board, b2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,5 +2,8 @@ mod api;
|
||||||
pub use api::{CoreApp, Request, Response};
|
pub use api::{CoreApp, Request, Response};
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{Color, Size};
|
pub use types::{BoardError, Color, Size};
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
|
mod board;
|
||||||
|
pub use board::*;
|
||||||
|
|
|
@ -1,20 +1,34 @@
|
||||||
use crate::api::PlayStoneRequest;
|
use crate::{
|
||||||
|
api::PlayStoneRequest,
|
||||||
|
board::{Board, Coordinate},
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Error)]
|
||||||
|
pub enum BoardError {
|
||||||
|
#[error("Position is invalid")]
|
||||||
|
InvalidPosition,
|
||||||
|
#[error("Self-capture is forbidden")]
|
||||||
|
SelfCapture,
|
||||||
|
#[error("Ko")]
|
||||||
|
Ko,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
Black,
|
Black,
|
||||||
White,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
pub width: u8,
|
pub width: usize,
|
||||||
pub height: u8,
|
pub height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Size {
|
impl Default for Size {
|
||||||
|
@ -41,7 +55,10 @@ impl AppState {
|
||||||
pub fn place_stone(&mut self, req: PlayStoneRequest) {
|
pub fn place_stone(&mut self, req: PlayStoneRequest) {
|
||||||
match self.game {
|
match self.game {
|
||||||
Some(ref mut game) => {
|
Some(ref mut game) => {
|
||||||
game.place_stone(req.column, req.row);
|
game.place_stone(Coordinate {
|
||||||
|
column: req.column,
|
||||||
|
row: req.row,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +99,8 @@ pub struct Player {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GameState {
|
pub struct GameState {
|
||||||
pub board: Board,
|
pub board: Board,
|
||||||
|
pub past_positions: Vec<Board>,
|
||||||
|
|
||||||
pub conversation: Vec<String>,
|
pub conversation: Vec<String>,
|
||||||
pub current_player: Color,
|
pub current_player: Color,
|
||||||
|
|
||||||
|
@ -96,6 +115,7 @@ impl GameState {
|
||||||
fn new() -> GameState {
|
fn new() -> GameState {
|
||||||
GameState {
|
GameState {
|
||||||
board: Board::new(),
|
board: Board::new(),
|
||||||
|
past_positions: vec![],
|
||||||
conversation: vec![],
|
conversation: vec![],
|
||||||
current_player: Color::Black,
|
current_player: Color::Black,
|
||||||
white_player: Player {
|
white_player: Player {
|
||||||
|
@ -111,47 +131,78 @@ impl GameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn place_stone(&mut self, column: u8, row: u8) {
|
fn place_stone(&mut self, coordinate: Coordinate) -> Result<(), BoardError> {
|
||||||
self.board.place_stone(column, row, self.current_player);
|
let board = self.board.clone();
|
||||||
|
let new_board = board.place_stone(coordinate, self.current_player)?;
|
||||||
|
|
||||||
|
if self.past_positions.contains(&new_board) {
|
||||||
|
return Err(BoardError::Ko);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.past_positions.push(self.board.clone());
|
||||||
|
self.board = new_board;
|
||||||
match self.current_player {
|
match self.current_player {
|
||||||
Color::White => self.current_player = Color::Black,
|
Color::White => self.current_player = Color::Black,
|
||||||
Color::Black => self.current_player = Color::White,
|
Color::Black => self.current_player = Color::White,
|
||||||
}
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[cfg(test)]
|
||||||
pub struct Board {
|
mod test {
|
||||||
pub size: Size,
|
use super::*;
|
||||||
pub spaces: Vec<Option<Color>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Board {
|
#[test]
|
||||||
fn new() -> Self {
|
fn current_player_changes_after_move() {
|
||||||
let mut spaces = Vec::new();
|
let mut state = GameState::new();
|
||||||
for _ in 0..19 * 19 {
|
assert_eq!(state.current_player, Color::Black);
|
||||||
spaces.push(None);
|
state.place_stone(Coordinate { column: 9, row: 9 }).unwrap();
|
||||||
}
|
assert_eq!(state.current_player, Color::White);
|
||||||
Self {
|
|
||||||
size: Size {
|
|
||||||
width: 19,
|
|
||||||
height: 19,
|
|
||||||
},
|
|
||||||
spaces,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn place_stone(&mut self, column: u8, row: u8, stone: Color) {
|
#[test]
|
||||||
let addr = self.addr(column, row);
|
fn current_player_remains_the_same_after_self_capture() {
|
||||||
self.spaces[addr] = Some(stone);
|
let mut state = GameState::new();
|
||||||
|
state.board = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 17, row: 0 }, Color::White),
|
||||||
|
(Coordinate { column: 17, row: 1 }, Color::White),
|
||||||
|
(Coordinate { column: 18, row: 1 }, Color::White),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
state.current_player = Color::Black;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
state.place_stone(Coordinate { column: 18, row: 0 }),
|
||||||
|
Err(BoardError::SelfCapture)
|
||||||
|
);
|
||||||
|
assert_eq!(state.current_player, Color::Black);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stone(&self, column: u8, row: u8) -> Option<Color> {
|
#[test]
|
||||||
let addr = self.addr(column, row);
|
fn ko_rules_are_enforced() {
|
||||||
self.spaces[addr]
|
let mut state = GameState::new();
|
||||||
}
|
state.board = Board::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 7, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 8 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 10 }, Color::White),
|
||||||
|
(Coordinate { column: 9, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 10, row: 9 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 8 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 10 }, Color::Black),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
fn addr(&self, column: u8, row: u8) -> usize {
|
state.place_stone(Coordinate { column: 8, row: 9 }).unwrap();
|
||||||
((row as usize) * (self.size.width as usize) + (column as usize)) as usize
|
assert_eq!(
|
||||||
|
state.place_stone(Coordinate { column: 9, row: 9 }),
|
||||||
|
Err(BoardError::Ko)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::types::Color;
|
use crate::{
|
||||||
use crate::{types::GameState, ui::types};
|
types::{Color, GameState},
|
||||||
|
ui::types,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::types::{Color, Size};
|
use crate::types::{Color, Size};
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{PlayStoneRequest, Request},
|
api::{PlayStoneRequest, Request},
|
||||||
types::Board,
|
Board, Coordinate,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
@ -18,12 +18,14 @@ pub struct Jitter {
|
||||||
pub struct StoneElement {
|
pub struct StoneElement {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub jitter: Jitter,
|
pub jitter: Jitter,
|
||||||
|
pub liberties: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StoneElement {
|
impl StoneElement {
|
||||||
pub fn new(color: Color) -> Self {
|
pub fn new(color: Color, liberties: Option<u8>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
color,
|
color,
|
||||||
|
liberties,
|
||||||
jitter: Jitter { x: 0, y: 0 },
|
jitter: Jitter { x: 0, y: 0 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,11 +65,6 @@ impl BoardElement {
|
||||||
Self { size, spaces }
|
Self { size, spaces }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stone_mut<'a>(&'a mut self, column: u8, row: u8) -> &'a mut IntersectionElement {
|
|
||||||
let addr = self.addr(column, row);
|
|
||||||
self.spaces.get_mut(addr).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stone(&self, column: u8, row: u8) -> IntersectionElement {
|
pub fn stone(&self, column: u8, row: u8) -> IntersectionElement {
|
||||||
let addr = self.addr(column, row);
|
let addr = self.addr(column, row);
|
||||||
self.spaces[addr]
|
self.spaces[addr]
|
||||||
|
@ -83,9 +80,10 @@ impl From<&Board> for BoardElement {
|
||||||
let spaces: Vec<IntersectionElement> = (0..board.size.height)
|
let spaces: Vec<IntersectionElement> = (0..board.size.height)
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
(0..board.size.width)
|
(0..board.size.width)
|
||||||
.map(|column| match board.stone(column, row) {
|
.map(|column| match board.stone(&Coordinate { column, row }) {
|
||||||
Some(color) => IntersectionElement::Filled(StoneElement {
|
Some(color) => IntersectionElement::Filled(StoneElement {
|
||||||
jitter: Jitter { x: 0, y: 0 },
|
jitter: Jitter { x: 0, y: 0 },
|
||||||
|
liberties: None,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
None => IntersectionElement::Empty(Request::PlayStoneRequest(
|
None => IntersectionElement::Empty(Request::PlayStoneRequest(
|
||||||
|
|
|
@ -577,6 +577,15 @@ dependencies = [
|
||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grid"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0634107a3a005070dd73e27e74ecb691a94e9e5ba7829f434db7fbf73a6b5c47"
|
||||||
|
dependencies = [
|
||||||
|
"no-std-compat",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gsk4"
|
name = "gsk4"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
@ -776,8 +785,9 @@ dependencies = [
|
||||||
name = "kifu-core"
|
name = "kifu-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"grid",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"thiserror",
|
||||||
"typeshare",
|
"typeshare",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -792,6 +802,7 @@ dependencies = [
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"image",
|
"image",
|
||||||
"kifu-core",
|
"kifu-core",
|
||||||
|
"screenplay",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -880,6 +891,12 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "no-std-compat"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
|
@ -1144,6 +1161,13 @@ version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "screenplay"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"gtk4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,8 +3,8 @@ name = "kifu-gtk"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# [features]
|
[features]
|
||||||
# screenplay = []
|
screenplay = []
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ gtk = { version = "0.6", package = "gtk4" }
|
||||||
image = { version = "0.24" }
|
image = { version = "0.24" }
|
||||||
kifu-core = { path = "../kifu-core" }
|
kifu-core = { path = "../kifu-core" }
|
||||||
tokio = { version = "1.26", features = [ "full" ] }
|
tokio = { version = "1.26", features = [ "full" ] }
|
||||||
# screenplay = { path = "../../screenplay" }
|
screenplay = { path = "../../screenplay" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
glib-build-tools = "0.17"
|
glib-build-tools = "0.17"
|
||||||
|
@ -25,8 +25,8 @@ glib-build-tools = "0.17"
|
||||||
name = "kifu-gtk"
|
name = "kifu-gtk"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
# [[bin]]
|
[[bin]]
|
||||||
# name = "screenplay"
|
name = "screenplay"
|
||||||
# path = "src/bin/screenplay.rs"
|
path = "src/bin/screenplay.rs"
|
||||||
# required-features = [ "screenplay" ]
|
required-features = [ "screenplay" ]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
release:
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
dev:
|
||||||
|
cargo watch -x 'run --bin kifu-gtk'
|
|
@ -1,36 +0,0 @@
|
||||||
{ pkgs, typeshare }:
|
|
||||||
let
|
|
||||||
standardOverride = attrs: {
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.gtk4
|
|
||||||
];
|
|
||||||
buildInputs = [
|
|
||||||
typeshare
|
|
||||||
];
|
|
||||||
verbose = true;
|
|
||||||
};
|
|
||||||
customBuildInfo = pkgs: pkgs.buildRustCrate.override {
|
|
||||||
defaultCrateOverrides = pkgs.defaultCrateOverrides // {
|
|
||||||
cairo-sys-rs = standardOverride;
|
|
||||||
graphene-sys = standardOverride;
|
|
||||||
gobject-sys = standardOverride;
|
|
||||||
pango-sys = standardOverride;
|
|
||||||
gio-sys = standardOverride;
|
|
||||||
gdk-pixbuf-sys = standardOverride;
|
|
||||||
gdk4-sys = standardOverride;
|
|
||||||
gsk4-sys = standardOverride;
|
|
||||||
gtk4-sys = standardOverride;
|
|
||||||
kifu-gtk = attrs: {
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.glib
|
|
||||||
typeshare
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in (import ./Cargo.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
buildRustCrateForPkgs = customBuildInfo;
|
|
||||||
release = true;
|
|
||||||
}).rootCrate.build
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
use kifu_core::{
|
||||||
|
ui::{ChatElement, PlayerCardElement, PlayingFieldView},
|
||||||
|
Color,
|
||||||
|
};
|
||||||
|
use kifu_gtk::ui::{playing_field_view, Chat, PlayerCard, PlayingField};
|
||||||
|
use screenplay::{Screen, Screenplay};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
|
||||||
|
.expect("Failed to register resources");
|
||||||
|
|
||||||
|
let app = gtk::Application::builder()
|
||||||
|
.application_id("com.luminescent-dreams.kifu-gtk.screenplay")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
app.connect_activate(|app| {
|
||||||
|
let screens = vec![
|
||||||
|
Screen {
|
||||||
|
title: "PlayerCard".to_owned(),
|
||||||
|
widget: PlayerCard::new(PlayerCardElement {
|
||||||
|
color: Color::Black,
|
||||||
|
name: "Opal".to_owned(),
|
||||||
|
rank: "10 kyu".to_owned(),
|
||||||
|
clock: "25:00".to_owned(),
|
||||||
|
})
|
||||||
|
.upcast::<gtk::Widget>(),
|
||||||
|
adjustments: vec![],
|
||||||
|
},
|
||||||
|
Screen {
|
||||||
|
title: "ChatArea".to_owned(),
|
||||||
|
widget: Chat::new(ChatElement {
|
||||||
|
messages: vec![
|
||||||
|
"message 1".to_owned(),
|
||||||
|
"message 2".to_owned(),
|
||||||
|
"message 3".to_owned(),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.upcast::<gtk::Widget>(),
|
||||||
|
adjustments: vec![],
|
||||||
|
},
|
||||||
|
Screen {
|
||||||
|
title: "PlayingField".to_owned(),
|
||||||
|
widget: PlayingField::new(playing_field_view()).upcast::<gtk::Widget>(),
|
||||||
|
adjustments: vec![],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
Screenplay::new(&app, screens).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.run();
|
||||||
|
}
|
|
@ -141,7 +141,7 @@ impl ObjectImpl for BoardPrivate {
|
||||||
(0..19).for_each(|row| {
|
(0..19).for_each(|row| {
|
||||||
match board.stone(row, col) {
|
match board.stone(row, col) {
|
||||||
IntersectionElement::Filled(stone) => {
|
IntersectionElement::Filled(stone) => {
|
||||||
pen.stone(&context, row, col, stone.color);
|
pen.stone(&context, row, col, stone.color, stone.liberties);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
@ -270,12 +270,27 @@ impl Pen {
|
||||||
let _ = context.fill();
|
let _ = context.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
|
fn stone(
|
||||||
|
&self,
|
||||||
|
context: &cairo::Context,
|
||||||
|
row: u8,
|
||||||
|
col: u8,
|
||||||
|
color: Color,
|
||||||
|
liberties: Option<u8>,
|
||||||
|
) {
|
||||||
match color {
|
match color {
|
||||||
Color::White => context.set_source_rgb(0.9, 0.9, 0.9),
|
Color::White => context.set_source_rgb(0.9, 0.9, 0.9),
|
||||||
Color::Black => context.set_source_rgb(0.0, 0.0, 0.0),
|
Color::Black => context.set_source_rgb(0.0, 0.0, 0.0),
|
||||||
};
|
};
|
||||||
self.draw_stone(context, row, col);
|
self.draw_stone(context, row, col);
|
||||||
|
|
||||||
|
if let Some(liberties) = liberties {
|
||||||
|
let stone_location = self.stone_location(row, col);
|
||||||
|
context.set_source_rgb(1., 0., 1.);
|
||||||
|
context.set_font_size(32.);
|
||||||
|
context.move_to(stone_location.0 - 10., stone_location.1 + 10.);
|
||||||
|
let _ = context.show_text(&format!("{}", liberties));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
|
fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
|
||||||
|
@ -288,13 +303,15 @@ impl Pen {
|
||||||
|
|
||||||
fn draw_stone(&self, context: &cairo::Context, row: u8, col: u8) {
|
fn draw_stone(&self, context: &cairo::Context, row: u8, col: u8) {
|
||||||
let radius = self.hspace_between / 2. - 2.;
|
let radius = self.hspace_between / 2. - 2.;
|
||||||
context.arc(
|
let (x_loc, y_loc) = self.stone_location(row, col);
|
||||||
self.x_offset + (col as f64) * self.hspace_between,
|
context.arc(x_loc, y_loc, radius, 0.0, 2.0 * std::f64::consts::PI);
|
||||||
self.y_offset + (row as f64) * self.vspace_between,
|
|
||||||
radius,
|
|
||||||
0.0,
|
|
||||||
2.0 * std::f64::consts::PI,
|
|
||||||
);
|
|
||||||
let _ = context.fill();
|
let _ = context.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stone_location(&self, row: u8, col: u8) -> (f64, f64) {
|
||||||
|
(
|
||||||
|
self.x_offset + (col as f64) * self.hspace_between,
|
||||||
|
self.y_offset + (row as f64) * self.vspace_between,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,14 +76,6 @@ impl PlayingField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "screenplay")]
|
|
||||||
use kifu_core::{
|
|
||||||
ui::{
|
|
||||||
BoardElement, ChatElement, IntersectionElement, PlayerCardElement, StoneElement,
|
|
||||||
TextFieldElement,
|
|
||||||
},
|
|
||||||
Color, Size,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "screenplay")]
|
#[cfg(feature = "screenplay")]
|
||||||
pub fn playing_field_view() -> PlayingFieldView {
|
pub fn playing_field_view() -> PlayingFieldView {
|
||||||
let mut board = BoardElement::new(Size {
|
let mut board = BoardElement::new(Size {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
dev:
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
test-server:
|
||||||
|
npx http-server ./dist
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
Generated by typeshare 1.5.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface PlayStoneRequest {
|
||||||
|
column: number;
|
||||||
|
row: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Size {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IntersectionElement =
|
||||||
|
| { type: "Unplayable", content?: undefined }
|
||||||
|
| { type: "Empty", content: Request }
|
||||||
|
| { type: "Filled", content: StoneElement };
|
||||||
|
|
||||||
|
export interface BoardElement {
|
||||||
|
size: Size;
|
||||||
|
spaces: IntersectionElement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Color {
|
||||||
|
Black = "Black",
|
||||||
|
White = "White",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlayerCardElement {
|
||||||
|
color: Color;
|
||||||
|
name: string;
|
||||||
|
rank: string;
|
||||||
|
clock: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatElement {
|
||||||
|
messages: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextFieldElement {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlayingFieldView {
|
||||||
|
board: BoardElement;
|
||||||
|
player_card_black: PlayerCardElement;
|
||||||
|
player_card_white: PlayerCardElement;
|
||||||
|
chat: ChatElement;
|
||||||
|
message: TextFieldElement;
|
||||||
|
current_player: Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Jitter {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StoneElement {
|
||||||
|
color: Color;
|
||||||
|
jitter: Jitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Request =
|
||||||
|
| { type: "PlayingField", content?: undefined }
|
||||||
|
| { type: "PlayStoneRequest", content: PlayStoneRequest };
|
||||||
|
|
||||||
|
export type Response =
|
||||||
|
| { type: "PlayingFieldView", content: PlayingFieldView };
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# This file has been generated by node2nix 1.11.1. Do not edit!
|
|
||||||
|
|
||||||
{pkgs ? import <nixpkgs> {
|
|
||||||
inherit system;
|
|
||||||
}, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-14_x"}:
|
|
||||||
|
|
||||||
let
|
|
||||||
nodeEnv = import ./node-env.nix {
|
|
||||||
inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript;
|
|
||||||
inherit pkgs nodejs;
|
|
||||||
libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
import ./node-packages.nix {
|
|
||||||
inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit;
|
|
||||||
inherit nodeEnv;
|
|
||||||
}
|
|
|
@ -1,689 +0,0 @@
|
||||||
# This file originates from node2nix
|
|
||||||
|
|
||||||
{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}:
|
|
||||||
|
|
||||||
let
|
|
||||||
# Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master
|
|
||||||
utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux;
|
|
||||||
|
|
||||||
python = if nodejs ? python then nodejs.python else python2;
|
|
||||||
|
|
||||||
# Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
|
|
||||||
tarWrapper = runCommand "tarWrapper" {} ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
|
|
||||||
cat > $out/bin/tar <<EOF
|
|
||||||
#! ${stdenv.shell} -e
|
|
||||||
$(type -p tar) "\$@" --warning=no-unknown-keyword --delay-directory-restore
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x $out/bin/tar
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Function that generates a TGZ file from a NPM project
|
|
||||||
buildNodeSourceDist =
|
|
||||||
{ name, version, src, ... }:
|
|
||||||
|
|
||||||
stdenv.mkDerivation {
|
|
||||||
name = "node-tarball-${name}-${version}";
|
|
||||||
inherit src;
|
|
||||||
buildInputs = [ nodejs ];
|
|
||||||
buildPhase = ''
|
|
||||||
export HOME=$TMPDIR
|
|
||||||
tgzFile=$(npm pack | tail -n 1) # Hooks to the pack command will add output (https://docs.npmjs.com/misc/scripts)
|
|
||||||
'';
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/tarballs
|
|
||||||
mv $tgzFile $out/tarballs
|
|
||||||
mkdir -p $out/nix-support
|
|
||||||
echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Common shell logic
|
|
||||||
installPackage = writeShellScript "install-package" ''
|
|
||||||
installPackage() {
|
|
||||||
local packageName=$1 src=$2
|
|
||||||
|
|
||||||
local strippedName
|
|
||||||
|
|
||||||
local DIR=$PWD
|
|
||||||
cd $TMPDIR
|
|
||||||
|
|
||||||
unpackFile $src
|
|
||||||
|
|
||||||
# Make the base dir in which the target dependency resides first
|
|
||||||
mkdir -p "$(dirname "$DIR/$packageName")"
|
|
||||||
|
|
||||||
if [ -f "$src" ]
|
|
||||||
then
|
|
||||||
# Figure out what directory has been unpacked
|
|
||||||
packageDir="$(find . -maxdepth 1 -type d | tail -1)"
|
|
||||||
|
|
||||||
# Restore write permissions to make building work
|
|
||||||
find "$packageDir" -type d -exec chmod u+x {} \;
|
|
||||||
chmod -R u+w "$packageDir"
|
|
||||||
|
|
||||||
# Move the extracted tarball into the output folder
|
|
||||||
mv "$packageDir" "$DIR/$packageName"
|
|
||||||
elif [ -d "$src" ]
|
|
||||||
then
|
|
||||||
# Get a stripped name (without hash) of the source directory.
|
|
||||||
# On old nixpkgs it's already set internally.
|
|
||||||
if [ -z "$strippedName" ]
|
|
||||||
then
|
|
||||||
strippedName="$(stripHash $src)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restore write permissions to make building work
|
|
||||||
chmod -R u+w "$strippedName"
|
|
||||||
|
|
||||||
# Move the extracted directory into the output folder
|
|
||||||
mv "$strippedName" "$DIR/$packageName"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Change to the package directory to install dependencies
|
|
||||||
cd "$DIR/$packageName"
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Bundle the dependencies of the package
|
|
||||||
#
|
|
||||||
# Only include dependencies if they don't exist. They may also be bundled in the package.
|
|
||||||
includeDependencies = {dependencies}:
|
|
||||||
lib.optionalString (dependencies != []) (
|
|
||||||
''
|
|
||||||
mkdir -p node_modules
|
|
||||||
cd node_modules
|
|
||||||
''
|
|
||||||
+ (lib.concatMapStrings (dependency:
|
|
||||||
''
|
|
||||||
if [ ! -e "${dependency.packageName}" ]; then
|
|
||||||
${composePackage dependency}
|
|
||||||
fi
|
|
||||||
''
|
|
||||||
) dependencies)
|
|
||||||
+ ''
|
|
||||||
cd ..
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
# Recursively composes the dependencies of a package
|
|
||||||
composePackage = { name, packageName, src, dependencies ? [], ... }@args:
|
|
||||||
builtins.addErrorContext "while evaluating node package '${packageName}'" ''
|
|
||||||
installPackage "${packageName}" "${src}"
|
|
||||||
${includeDependencies { inherit dependencies; }}
|
|
||||||
cd ..
|
|
||||||
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
||||||
'';
|
|
||||||
|
|
||||||
pinpointDependencies = {dependencies, production}:
|
|
||||||
let
|
|
||||||
pinpointDependenciesFromPackageJSON = writeTextFile {
|
|
||||||
name = "pinpointDependencies.js";
|
|
||||||
text = ''
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
function resolveDependencyVersion(location, name) {
|
|
||||||
if(location == process.env['NIX_STORE']) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json");
|
|
||||||
|
|
||||||
if(fs.existsSync(dependencyPackageJSON)) {
|
|
||||||
var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON));
|
|
||||||
|
|
||||||
if(dependencyPackageObj.name == name) {
|
|
||||||
return dependencyPackageObj.version;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return resolveDependencyVersion(path.resolve(location, ".."), name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceDependencies(dependencies) {
|
|
||||||
if(typeof dependencies == "object" && dependencies !== null) {
|
|
||||||
for(var dependency in dependencies) {
|
|
||||||
var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency);
|
|
||||||
|
|
||||||
if(resolvedVersion === null) {
|
|
||||||
process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n");
|
|
||||||
} else {
|
|
||||||
dependencies[dependency] = resolvedVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read the package.json configuration */
|
|
||||||
var packageObj = JSON.parse(fs.readFileSync('./package.json'));
|
|
||||||
|
|
||||||
/* Pinpoint all dependencies */
|
|
||||||
replaceDependencies(packageObj.dependencies);
|
|
||||||
if(process.argv[2] == "development") {
|
|
||||||
replaceDependencies(packageObj.devDependencies);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
packageObj.devDependencies = {};
|
|
||||||
}
|
|
||||||
replaceDependencies(packageObj.optionalDependencies);
|
|
||||||
replaceDependencies(packageObj.peerDependencies);
|
|
||||||
|
|
||||||
/* Write the fixed package.json file */
|
|
||||||
fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2));
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
''
|
|
||||||
node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"}
|
|
||||||
|
|
||||||
${lib.optionalString (dependencies != [])
|
|
||||||
''
|
|
||||||
if [ -d node_modules ]
|
|
||||||
then
|
|
||||||
cd node_modules
|
|
||||||
${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies}
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
''}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Recursively traverses all dependencies of a package and pinpoints all
|
|
||||||
# dependencies in the package.json file to the versions that are actually
|
|
||||||
# being used.
|
|
||||||
|
|
||||||
pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args:
|
|
||||||
''
|
|
||||||
if [ -d "${packageName}" ]
|
|
||||||
then
|
|
||||||
cd "${packageName}"
|
|
||||||
${pinpointDependencies { inherit dependencies production; }}
|
|
||||||
cd ..
|
|
||||||
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Extract the Node.js source code which is used to compile packages with
|
|
||||||
# native bindings
|
|
||||||
nodeSources = runCommand "node-sources" {} ''
|
|
||||||
tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
|
|
||||||
mv node-* $out
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty)
|
|
||||||
addIntegrityFieldsScript = writeTextFile {
|
|
||||||
name = "addintegrityfields.js";
|
|
||||||
text = ''
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
function augmentDependencies(baseDir, dependencies) {
|
|
||||||
for(var dependencyName in dependencies) {
|
|
||||||
var dependency = dependencies[dependencyName];
|
|
||||||
|
|
||||||
// Open package.json and augment metadata fields
|
|
||||||
var packageJSONDir = path.join(baseDir, "node_modules", dependencyName);
|
|
||||||
var packageJSONPath = path.join(packageJSONDir, "package.json");
|
|
||||||
|
|
||||||
if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored
|
|
||||||
console.log("Adding metadata fields to: "+packageJSONPath);
|
|
||||||
var packageObj = JSON.parse(fs.readFileSync(packageJSONPath));
|
|
||||||
|
|
||||||
if(dependency.integrity) {
|
|
||||||
packageObj["_integrity"] = dependency.integrity;
|
|
||||||
} else {
|
|
||||||
packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads.
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dependency.resolved) {
|
|
||||||
packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided
|
|
||||||
} else {
|
|
||||||
packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories.
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dependency.from !== undefined) { // Adopt from property if one has been provided
|
|
||||||
packageObj["_from"] = dependency.from;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Augment transitive dependencies
|
|
||||||
if(dependency.dependencies !== undefined) {
|
|
||||||
augmentDependencies(packageJSONDir, dependency.dependencies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fs.existsSync("./package-lock.json")) {
|
|
||||||
var packageLock = JSON.parse(fs.readFileSync("./package-lock.json"));
|
|
||||||
|
|
||||||
if(![1, 2].includes(packageLock.lockfileVersion)) {
|
|
||||||
process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(packageLock.dependencies !== undefined) {
|
|
||||||
augmentDependencies(".", packageLock.dependencies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes
|
|
||||||
reconstructPackageLock = writeTextFile {
|
|
||||||
name = "reconstructpackagelock.js";
|
|
||||||
text = ''
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
var packageObj = JSON.parse(fs.readFileSync("package.json"));
|
|
||||||
|
|
||||||
var lockObj = {
|
|
||||||
name: packageObj.name,
|
|
||||||
version: packageObj.version,
|
|
||||||
lockfileVersion: 2,
|
|
||||||
requires: true,
|
|
||||||
packages: {
|
|
||||||
"": {
|
|
||||||
name: packageObj.name,
|
|
||||||
version: packageObj.version,
|
|
||||||
license: packageObj.license,
|
|
||||||
bin: packageObj.bin,
|
|
||||||
dependencies: packageObj.dependencies,
|
|
||||||
engines: packageObj.engines,
|
|
||||||
optionalDependencies: packageObj.optionalDependencies
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dependencies: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
function augmentPackageJSON(filePath, packages, dependencies) {
|
|
||||||
var packageJSON = path.join(filePath, "package.json");
|
|
||||||
if(fs.existsSync(packageJSON)) {
|
|
||||||
var packageObj = JSON.parse(fs.readFileSync(packageJSON));
|
|
||||||
packages[filePath] = {
|
|
||||||
version: packageObj.version,
|
|
||||||
integrity: "sha1-000000000000000000000000000=",
|
|
||||||
dependencies: packageObj.dependencies,
|
|
||||||
engines: packageObj.engines,
|
|
||||||
optionalDependencies: packageObj.optionalDependencies
|
|
||||||
};
|
|
||||||
dependencies[packageObj.name] = {
|
|
||||||
version: packageObj.version,
|
|
||||||
integrity: "sha1-000000000000000000000000000=",
|
|
||||||
dependencies: {}
|
|
||||||
};
|
|
||||||
processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function processDependencies(dir, packages, dependencies) {
|
|
||||||
if(fs.existsSync(dir)) {
|
|
||||||
var files = fs.readdirSync(dir);
|
|
||||||
|
|
||||||
files.forEach(function(entry) {
|
|
||||||
var filePath = path.join(dir, entry);
|
|
||||||
var stats = fs.statSync(filePath);
|
|
||||||
|
|
||||||
if(stats.isDirectory()) {
|
|
||||||
if(entry.substr(0, 1) == "@") {
|
|
||||||
// When we encounter a namespace folder, augment all packages belonging to the scope
|
|
||||||
var pkgFiles = fs.readdirSync(filePath);
|
|
||||||
|
|
||||||
pkgFiles.forEach(function(entry) {
|
|
||||||
if(stats.isDirectory()) {
|
|
||||||
var pkgFilePath = path.join(filePath, entry);
|
|
||||||
augmentPackageJSON(pkgFilePath, packages, dependencies);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
augmentPackageJSON(filePath, packages, dependencies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processDependencies("node_modules", lockObj.packages, lockObj.dependencies);
|
|
||||||
|
|
||||||
fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2));
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Script that links bins defined in package.json to the node_modules bin directory
|
|
||||||
# NPM does not do this for top-level packages itself anymore as of v7
|
|
||||||
linkBinsScript = writeTextFile {
|
|
||||||
name = "linkbins.js";
|
|
||||||
text = ''
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
var packageObj = JSON.parse(fs.readFileSync("package.json"));
|
|
||||||
|
|
||||||
var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep);
|
|
||||||
|
|
||||||
if(packageObj.bin !== undefined) {
|
|
||||||
fs.mkdirSync(path.join(nodeModules, ".bin"))
|
|
||||||
|
|
||||||
if(typeof packageObj.bin == "object") {
|
|
||||||
Object.keys(packageObj.bin).forEach(function(exe) {
|
|
||||||
if(fs.existsSync(packageObj.bin[exe])) {
|
|
||||||
console.log("linking bin '" + exe + "'");
|
|
||||||
fs.symlinkSync(
|
|
||||||
path.join("..", packageObj.name, packageObj.bin[exe]),
|
|
||||||
path.join(nodeModules, ".bin", exe)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("skipping non-existent bin '" + exe + "'");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(fs.existsSync(packageObj.bin)) {
|
|
||||||
console.log("linking bin '" + packageObj.bin + "'");
|
|
||||||
fs.symlinkSync(
|
|
||||||
path.join("..", packageObj.name, packageObj.bin),
|
|
||||||
path.join(nodeModules, ".bin", packageObj.name.split("/").pop())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("skipping non-existent bin '" + packageObj.bin + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) {
|
|
||||||
fs.mkdirSync(path.join(nodeModules, ".bin"))
|
|
||||||
|
|
||||||
fs.readdirSync(packageObj.directories.bin).forEach(function(exe) {
|
|
||||||
if(fs.existsSync(path.join(packageObj.directories.bin, exe))) {
|
|
||||||
console.log("linking bin '" + exe + "'");
|
|
||||||
fs.symlinkSync(
|
|
||||||
path.join("..", packageObj.name, packageObj.directories.bin, exe),
|
|
||||||
path.join(nodeModules, ".bin", exe)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("skipping non-existent bin '" + exe + "'");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}:
|
|
||||||
let
|
|
||||||
forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com";
|
|
||||||
in
|
|
||||||
''
|
|
||||||
# Pinpoint the versions of all dependencies to the ones that are actually being used
|
|
||||||
echo "pinpointing versions of dependencies..."
|
|
||||||
source $pinpointDependenciesScriptPath
|
|
||||||
|
|
||||||
# Patch the shebangs of the bundled modules to prevent them from
|
|
||||||
# calling executables outside the Nix store as much as possible
|
|
||||||
patchShebangs .
|
|
||||||
|
|
||||||
# Deploy the Node.js package by running npm install. Since the
|
|
||||||
# dependencies have been provided already by ourselves, it should not
|
|
||||||
# attempt to install them again, which is good, because we want to make
|
|
||||||
# it Nix's responsibility. If it needs to install any dependencies
|
|
||||||
# anyway (e.g. because the dependency parameters are
|
|
||||||
# incomplete/incorrect), it fails.
|
|
||||||
#
|
|
||||||
# The other responsibilities of NPM are kept -- version checks, build
|
|
||||||
# steps, postprocessing etc.
|
|
||||||
|
|
||||||
export HOME=$TMPDIR
|
|
||||||
cd "${packageName}"
|
|
||||||
runHook preRebuild
|
|
||||||
|
|
||||||
${lib.optionalString bypassCache ''
|
|
||||||
${lib.optionalString reconstructLock ''
|
|
||||||
if [ -f package-lock.json ]
|
|
||||||
then
|
|
||||||
echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!"
|
|
||||||
echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!"
|
|
||||||
rm package-lock.json
|
|
||||||
else
|
|
||||||
echo "No package-lock.json file found, reconstructing..."
|
|
||||||
fi
|
|
||||||
|
|
||||||
node ${reconstructPackageLock}
|
|
||||||
''}
|
|
||||||
|
|
||||||
node ${addIntegrityFieldsScript}
|
|
||||||
''}
|
|
||||||
|
|
||||||
npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild
|
|
||||||
|
|
||||||
runHook postRebuild
|
|
||||||
|
|
||||||
if [ "''${dontNpmInstall-}" != "1" ]
|
|
||||||
then
|
|
||||||
# NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
|
|
||||||
rm -f npm-shrinkwrap.json
|
|
||||||
|
|
||||||
npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Link executables defined in package.json
|
|
||||||
node ${linkBinsScript}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Builds and composes an NPM package including all its dependencies
|
|
||||||
buildNodePackage =
|
|
||||||
{ name
|
|
||||||
, packageName
|
|
||||||
, version ? null
|
|
||||||
, dependencies ? []
|
|
||||||
, buildInputs ? []
|
|
||||||
, production ? true
|
|
||||||
, npmFlags ? ""
|
|
||||||
, dontNpmInstall ? false
|
|
||||||
, bypassCache ? false
|
|
||||||
, reconstructLock ? false
|
|
||||||
, preRebuild ? ""
|
|
||||||
, dontStrip ? true
|
|
||||||
, unpackPhase ? "true"
|
|
||||||
, buildPhase ? "true"
|
|
||||||
, meta ? {}
|
|
||||||
, ... }@args:
|
|
||||||
|
|
||||||
let
|
|
||||||
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" "meta" ];
|
|
||||||
in
|
|
||||||
stdenv.mkDerivation ({
|
|
||||||
name = "${name}${if version == null then "" else "-${version}"}";
|
|
||||||
buildInputs = [ tarWrapper python nodejs ]
|
|
||||||
++ lib.optional (stdenv.isLinux) utillinux
|
|
||||||
++ lib.optional (stdenv.isDarwin) libtool
|
|
||||||
++ buildInputs;
|
|
||||||
|
|
||||||
inherit nodejs;
|
|
||||||
|
|
||||||
inherit dontStrip; # Stripping may fail a build for some package deployments
|
|
||||||
inherit dontNpmInstall preRebuild unpackPhase buildPhase;
|
|
||||||
|
|
||||||
compositionScript = composePackage args;
|
|
||||||
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
|
|
||||||
|
|
||||||
passAsFile = [ "compositionScript" "pinpointDependenciesScript" ];
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
source ${installPackage}
|
|
||||||
|
|
||||||
# Create and enter a root node_modules/ folder
|
|
||||||
mkdir -p $out/lib/node_modules
|
|
||||||
cd $out/lib/node_modules
|
|
||||||
|
|
||||||
# Compose the package and all its dependencies
|
|
||||||
source $compositionScriptPath
|
|
||||||
|
|
||||||
${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
|
|
||||||
|
|
||||||
# Create symlink to the deployed executable folder, if applicable
|
|
||||||
if [ -d "$out/lib/node_modules/.bin" ]
|
|
||||||
then
|
|
||||||
ln -s $out/lib/node_modules/.bin $out/bin
|
|
||||||
|
|
||||||
# Fixup all executables
|
|
||||||
ls $out/bin/* | while read i
|
|
||||||
do
|
|
||||||
file="$(readlink -f "$i")"
|
|
||||||
chmod u+rwx "$file"
|
|
||||||
if isScript "$file"
|
|
||||||
then
|
|
||||||
sed -i 's/\r$//' "$file" # convert crlf to lf
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create symlinks to the deployed manual page folders, if applicable
|
|
||||||
if [ -d "$out/lib/node_modules/${packageName}/man" ]
|
|
||||||
then
|
|
||||||
mkdir -p $out/share
|
|
||||||
for dir in "$out/lib/node_modules/${packageName}/man/"*
|
|
||||||
do
|
|
||||||
mkdir -p $out/share/man/$(basename "$dir")
|
|
||||||
for page in "$dir"/*
|
|
||||||
do
|
|
||||||
ln -s $page $out/share/man/$(basename "$dir")
|
|
||||||
done
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run post install hook, if provided
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
# default to Node.js' platforms
|
|
||||||
platforms = nodejs.meta.platforms;
|
|
||||||
} // meta;
|
|
||||||
} // extraArgs);
|
|
||||||
|
|
||||||
# Builds a node environment (a node_modules folder and a set of binaries)
|
|
||||||
buildNodeDependencies =
|
|
||||||
{ name
|
|
||||||
, packageName
|
|
||||||
, version ? null
|
|
||||||
, src
|
|
||||||
, dependencies ? []
|
|
||||||
, buildInputs ? []
|
|
||||||
, production ? true
|
|
||||||
, npmFlags ? ""
|
|
||||||
, dontNpmInstall ? false
|
|
||||||
, bypassCache ? false
|
|
||||||
, reconstructLock ? false
|
|
||||||
, dontStrip ? true
|
|
||||||
, unpackPhase ? "true"
|
|
||||||
, buildPhase ? "true"
|
|
||||||
, ... }@args:
|
|
||||||
|
|
||||||
let
|
|
||||||
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ];
|
|
||||||
in
|
|
||||||
stdenv.mkDerivation ({
|
|
||||||
name = "node-dependencies-${name}${if version == null then "" else "-${version}"}";
|
|
||||||
|
|
||||||
buildInputs = [ tarWrapper python nodejs ]
|
|
||||||
++ lib.optional (stdenv.isLinux) utillinux
|
|
||||||
++ lib.optional (stdenv.isDarwin) libtool
|
|
||||||
++ buildInputs;
|
|
||||||
|
|
||||||
inherit dontStrip; # Stripping may fail a build for some package deployments
|
|
||||||
inherit dontNpmInstall unpackPhase buildPhase;
|
|
||||||
|
|
||||||
includeScript = includeDependencies { inherit dependencies; };
|
|
||||||
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
|
|
||||||
|
|
||||||
passAsFile = [ "includeScript" "pinpointDependenciesScript" ];
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
source ${installPackage}
|
|
||||||
|
|
||||||
mkdir -p $out/${packageName}
|
|
||||||
cd $out/${packageName}
|
|
||||||
|
|
||||||
source $includeScriptPath
|
|
||||||
|
|
||||||
# Create fake package.json to make the npm commands work properly
|
|
||||||
cp ${src}/package.json .
|
|
||||||
chmod 644 package.json
|
|
||||||
${lib.optionalString bypassCache ''
|
|
||||||
if [ -f ${src}/package-lock.json ]
|
|
||||||
then
|
|
||||||
cp ${src}/package-lock.json .
|
|
||||||
chmod 644 package-lock.json
|
|
||||||
fi
|
|
||||||
''}
|
|
||||||
|
|
||||||
# Go to the parent folder to make sure that all packages are pinpointed
|
|
||||||
cd ..
|
|
||||||
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
||||||
|
|
||||||
${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
|
|
||||||
|
|
||||||
# Expose the executables that were installed
|
|
||||||
cd ..
|
|
||||||
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
||||||
|
|
||||||
mv ${packageName} lib
|
|
||||||
ln -s $out/lib/node_modules/.bin $out/bin
|
|
||||||
'';
|
|
||||||
} // extraArgs);
|
|
||||||
|
|
||||||
# Builds a development shell
|
|
||||||
buildNodeShell =
|
|
||||||
{ name
|
|
||||||
, packageName
|
|
||||||
, version ? null
|
|
||||||
, src
|
|
||||||
, dependencies ? []
|
|
||||||
, buildInputs ? []
|
|
||||||
, production ? true
|
|
||||||
, npmFlags ? ""
|
|
||||||
, dontNpmInstall ? false
|
|
||||||
, bypassCache ? false
|
|
||||||
, reconstructLock ? false
|
|
||||||
, dontStrip ? true
|
|
||||||
, unpackPhase ? "true"
|
|
||||||
, buildPhase ? "true"
|
|
||||||
, ... }@args:
|
|
||||||
|
|
||||||
let
|
|
||||||
nodeDependencies = buildNodeDependencies args;
|
|
||||||
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "unpackPhase" "buildPhase" ];
|
|
||||||
in
|
|
||||||
stdenv.mkDerivation ({
|
|
||||||
name = "node-shell-${name}${if version == null then "" else "-${version}"}";
|
|
||||||
|
|
||||||
buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs;
|
|
||||||
buildCommand = ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
cat > $out/bin/shell <<EOF
|
|
||||||
#! ${stdenv.shell} -e
|
|
||||||
$shellHook
|
|
||||||
exec ${stdenv.shell}
|
|
||||||
EOF
|
|
||||||
chmod +x $out/bin/shell
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Provide the dependencies in a development shell through the NODE_PATH environment variable
|
|
||||||
inherit nodeDependencies;
|
|
||||||
shellHook = lib.optionalString (dependencies != []) ''
|
|
||||||
export NODE_PATH=${nodeDependencies}/lib/node_modules
|
|
||||||
export PATH="${nodeDependencies}/bin:$PATH"
|
|
||||||
'';
|
|
||||||
} // extraArgs);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
buildNodeSourceDist = lib.makeOverridable buildNodeSourceDist;
|
|
||||||
buildNodePackage = lib.makeOverridable buildNodePackage;
|
|
||||||
buildNodeDependencies = lib.makeOverridable buildNodeDependencies;
|
|
||||||
buildNodeShell = lib.makeOverridable buildNodeShell;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "kifu-pwa",
|
"name": "kifu-pwa",
|
||||||
"version": "0.0.1",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "webpack.config.js",
|
"main": "webpack.config.js",
|
||||||
"dependencies": {
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "webpack"
|
||||||
|
},
|
||||||
|
"author": "Savanni D'Gerinel <savanni@luminescent-dreams.com>",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"devDependencies": {
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/lodash": "^4.14.194",
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"ts-loader": "^9.4.2",
|
"ts-loader": "^9.4.2",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"webpack": "^5.80.0"
|
"webpack": "^5.82.0",
|
||||||
|
"webpack-cli": "^5.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21"
|
||||||
"webpack-cli": "^5.0.2"
|
}
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack",
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "GPL-3.0-or-later"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// import init, { CoreApp } from "kifu-wasm";
|
// import init, { CoreApp } from "./kifu_wasm.js";
|
||||||
|
|
||||||
const inputField = document.getElementById("input-temp") as HTMLInputElement;
|
const inputField = document.getElementById("input-temp");
|
||||||
const fromUnitField = document.getElementById("input-unit") as HTMLInputElement;
|
const fromUnitField = document.getElementById("input-unit");
|
||||||
const toUnitField = document.getElementById("output-unit") as HTMLInputElement;
|
const toUnitField = document.getElementById("output-unit");
|
||||||
const outputField = document.getElementById("output-temp") as HTMLInputElement;
|
const outputField = document.getElementById("output-temp");
|
||||||
const form = document.getElementById("converter") as HTMLFormElement;
|
const form = document.getElementById("converter");
|
||||||
|
|
||||||
function convertTemp(value: number, fromUnit: string, toUnit: string) {
|
function convertTemp(value, fromUnit, toUnit) {
|
||||||
if (fromUnit === "c") {
|
if (fromUnit === "c") {
|
||||||
if (toUnit === "f") {
|
if (toUnit === "f") {
|
||||||
return (value * 9) / 5 + 32;
|
return (value * 9) / 5 + 32;
|
||||||
|
@ -34,7 +34,7 @@ function convertTemp(value: number, fromUnit: string, toUnit: string) {
|
||||||
throw new Error("Invalid unit");
|
throw new Error("Invalid unit");
|
||||||
}
|
}
|
||||||
|
|
||||||
form?.addEventListener("input", () => {
|
form.addEventListener("input", () => {
|
||||||
const inputTemp = parseFloat(inputField.value);
|
const inputTemp = parseFloat(inputField.value);
|
||||||
const fromUnit = fromUnitField.value;
|
const fromUnit = fromUnitField.value;
|
||||||
const toUnit = toUnitField.value;
|
const toUnit = toUnitField.value;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
<link rel="stylesheet" href="converter.css">
|
<link rel="stylesheet" href="converter.css">
|
||||||
<title>Temperature converter</title>
|
<title>Temperature converter</title>
|
||||||
<script type="module" src="/dist/app.js"></script>
|
<script type="module" src="converter.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1> Temperature Converter </h1>
|
<h1> Temperature Converter </h1>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
if('serviceWorker' in navigator) {
|
if('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('/dist/sw.js', { scope: '/' });
|
navigator.serviceWorker.register('/sw.js', { scope: '/' });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
|
@ -5,10 +5,10 @@ self.addEventListener('install', event => {
|
||||||
const cache = await caches.open(CACHE_NAME);
|
const cache = await caches.open(CACHE_NAME);
|
||||||
cache.addAll([
|
cache.addAll([
|
||||||
'/',
|
'/',
|
||||||
'/converter.js',
|
'/kifu-bundle.js',
|
||||||
'/converter.css',
|
'/converter.css',
|
||||||
'/kifu_wasm.js',
|
// '/kifu_wasm.js',
|
||||||
'/kifu_wasm_bg.wasm',
|
// '/kifu_wasm_bg.wasm',
|
||||||
]);
|
]);
|
||||||
})());
|
})());
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2016",
|
|
||||||
"module": "commonjs",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,23 @@
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'development',
|
mode: "development",
|
||||||
entry: {
|
entry: {
|
||||||
app: './src/converter.ts',
|
"kifu-bundle": "./src/converter.ts",
|
||||||
sw: './src/sw.js',
|
sw: "./src/sw.js",
|
||||||
},
|
},
|
||||||
module: {
|
loader: {
|
||||||
rules: [
|
rules: [
|
||||||
{ test: /.ts$/, use: 'ts-loader', exclude: /node_modules/ },
|
{ test: /.ts$/, use: "ts-loader", exclude: /node_modules/ },
|
||||||
{ test: /\.wasm$/, type: 'asset/inline' }
|
{ test: /\.wasm$/, type: "asset/inline" }
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
plugins: [
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
{ from: "src/index.html" },
|
||||||
|
{ from: "src/converter.css" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,19 +0,0 @@
|
||||||
{ pkgs, typeshare, wasm-pack, rustc }:
|
|
||||||
let
|
|
||||||
customBuildInfo = pkgs: pkgs.buildRustCrate.override {
|
|
||||||
inherit rustc;
|
|
||||||
defaultCrateOverrides = pkgs.defaultCrateOverrides // {
|
|
||||||
kifu-wasm = attrs: {
|
|
||||||
buildInputs = [
|
|
||||||
typeshare
|
|
||||||
];
|
|
||||||
extraRustcOpts = [ "--target wasm32-unknown-unknown" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in (import ./Cargo.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
buildRustCrateForPkgs = customBuildInfo;
|
|
||||||
release = true;
|
|
||||||
}).rootCrate.build
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2016",
|
||||||
|
"module": "commonjs",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue