commit d8b781011bcf30f6fe044a67e1de1280612b2699
Author: Eason <30045503+Eason0729@users.noreply.github.com>
Date: Wed Jun 12 16:09:12 2024 +0800
reinit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5b057a6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+__pycache__
+/output1
+/output2
+dataset
+*.so
+/train
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..cd3dfc4
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,3590 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "ash"
+version = "0.37.3+1.3.251"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
+dependencies = [
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bincode"
+version = "2.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "burn"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3960b57a6ad4baf54d1dba766965e4559c4b9a8f391107fee5de29db57265840"
+dependencies = [
+ "burn-core",
+ "burn-train",
+]
+
+[[package]]
+name = "burn-autodiff"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9479c28bdce3f2b1541f0a9215628f6256b5f3d66871192a3c56d55171d28e"
+dependencies = [
+ "burn-common",
+ "burn-tensor",
+ "derive-new",
+ "log",
+ "spin",
+]
+
+[[package]]
+name = "burn-candle"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d811c54fa6d9beb38808a1aabd9515c39090720cae572d54f25c041b1702e8fd"
+dependencies = [
+ "burn-tensor",
+ "candle-core",
+ "derive-new",
+ "half",
+]
+
+[[package]]
+name = "burn-common"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9540b2f45a2d337220e702d7a87572c8e1c78db91a200b22924a8c4a6e9be4"
+dependencies = [
+ "async-trait",
+ "derive-new",
+ "getrandom",
+ "rand",
+ "serde",
+ "spin",
+ "uuid",
+ "web-time",
+]
+
+[[package]]
+name = "burn-compute"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e890d8999b25a1a090c2afe198243fc79f0a299efb531a4871c084b0ab9fa11"
+dependencies = [
+ "burn-common",
+ "derive-new",
+ "dirs",
+ "hashbrown 0.14.5",
+ "log",
+ "md5",
+ "serde",
+ "serde_json",
+ "spin",
+ "web-time",
+]
+
+[[package]]
+name = "burn-core"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8af6bc0afe55a57ff0b08f52302df4e3d09f96805a4f1e15c521f1082cb02b4f"
+dependencies = [
+ "bincode 2.0.0-rc.3",
+ "burn-autodiff",
+ "burn-candle",
+ "burn-common",
+ "burn-dataset",
+ "burn-derive",
+ "burn-ndarray",
+ "burn-tch",
+ "burn-tensor",
+ "burn-wgpu",
+ "derive-new",
+ "flate2",
+ "half",
+ "hashbrown 0.14.5",
+ "log",
+ "num-traits",
+ "rand",
+ "rmp-serde",
+ "serde",
+ "serde_json",
+ "spin",
+]
+
+[[package]]
+name = "burn-dataset"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3feae7766b56e947d38ac4d6903388270d848609339a147a513145703426f6db"
+dependencies = [
+ "csv",
+ "derive-new",
+ "dirs",
+ "gix-tempfile",
+ "image",
+ "r2d2",
+ "r2d2_sqlite",
+ "rand",
+ "rmp-serde",
+ "rusqlite",
+ "sanitize-filename",
+ "serde",
+ "serde_json",
+ "serde_rusqlite",
+ "strum",
+ "strum_macros",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "burn-derive"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8618ac2c171c7054ffd3ce8da15c3d4b11dc805eb393065c74c05882ef79d931"
+dependencies = [
+ "derive-new",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "burn-fusion"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d77b882d131a67d15f91b915fb3e0a5add73547e7352310d33c877fbe77c79e"
+dependencies = [
+ "burn-common",
+ "burn-tensor",
+ "derive-new",
+ "hashbrown 0.14.5",
+ "log",
+ "serde",
+ "spin",
+]
+
+[[package]]
+name = "burn-jit"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cb62a93030a690c329b95c01b43e3064a4bd36031e9111d537641d36e42f3ac"
+dependencies = [
+ "burn-common",
+ "burn-compute",
+ "burn-fusion",
+ "burn-tensor",
+ "bytemuck",
+ "derive-new",
+ "hashbrown 0.14.5",
+ "log",
+ "num-traits",
+ "rand",
+ "serde",
+ "spin",
+ "text_placeholder",
+]
+
+[[package]]
+name = "burn-ndarray"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f40bb0b5938937a721045752f1ec1baee8a873429fd17e6e6f2155c6cdf33a"
+dependencies = [
+ "burn-autodiff",
+ "burn-common",
+ "burn-tensor",
+ "derive-new",
+ "libm",
+ "matrixmultiply",
+ "ndarray",
+ "num-traits",
+ "rand",
+ "rayon",
+ "spin",
+]
+
+[[package]]
+name = "burn-tch"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb9c2b547499a3d990e93b950965b9a478edfec4a7bf98d5d4412ff8c897129"
+dependencies = [
+ "burn-tensor",
+ "half",
+ "libc",
+ "rand",
+ "tch",
+]
+
+[[package]]
+name = "burn-tensor"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa19c21f54e1a189be3bbaec45efafdf1c89b2763710b381c9f32ae25e7dbe8"
+dependencies = [
+ "burn-common",
+ "derive-new",
+ "half",
+ "hashbrown 0.14.5",
+ "num-traits",
+ "rand",
+ "rand_distr",
+ "serde",
+]
+
+[[package]]
+name = "burn-train"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a0014ee82ef967bd82dda378cfaf340f255c39c729e29ac3bc65d3107e4c7ee"
+dependencies = [
+ "burn-core",
+ "crossterm",
+ "derive-new",
+ "log",
+ "nvml-wrapper",
+ "ratatui",
+ "serde",
+ "sysinfo",
+ "systemstat",
+ "tracing-appender",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "burn-wgpu"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1575890471123109c6aeb725c52ac649fa9e0013e2303f57dc534d5e0cb857e5"
+dependencies = [
+ "burn-common",
+ "burn-compute",
+ "burn-fusion",
+ "burn-jit",
+ "burn-tensor",
+ "bytemuck",
+ "derive-new",
+ "futures-intrusive",
+ "hashbrown 0.14.5",
+ "log",
+ "pollster",
+ "wgpu",
+]
+
+[[package]]
+name = "bytemuck"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytesize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
+
+[[package]]
+name = "bzip2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "candle-core"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f1b20174c1707e20f4cb364a355b449803c03e9b0c9193324623cf9787a4e00"
+dependencies = [
+ "byteorder",
+ "gemm",
+ "half",
+ "memmap2",
+ "num-traits",
+ "num_cpus",
+ "rand",
+ "rand_distr",
+ "rayon",
+ "safetensors 0.4.3",
+ "thiserror",
+ "yoke",
+ "zip",
+]
+
+[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
+name = "cc"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
+dependencies = [
+ "jobserver",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "com"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
+dependencies = [
+ "com_macros",
+]
+
+[[package]]
+name = "com_macros"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
+dependencies = [
+ "com_macros_support",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "com_macros_support"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
+name = "crossterm"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
+dependencies = [
+ "bitflags 2.5.0",
+ "crossterm_winapi",
+ "libc",
+ "mio",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "csv"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "d3d12"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307"
+dependencies = [
+ "bitflags 2.5.0",
+ "libloading 0.8.3",
+ "winapi",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
+dependencies = [
+ "cfg-if",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive-new"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dirs"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "dyn-stack"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e53799688f5632f364f8fb387488dd05db9fe45db7011be066fc20e7027f8b"
+dependencies = [
+ "bytemuck",
+ "reborrow",
+]
+
+[[package]]
+name = "either"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+
+[[package]]
+name = "enum-as-inner"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "exr"
+version = "1.72.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
+dependencies = [
+ "bit_field",
+ "flume",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "faster-hex"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "flume"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-intrusive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
+dependencies = [
+ "futures-core",
+ "lock_api",
+ "parking_lot",
+]
+
+[[package]]
+name = "gemm"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab24cc62135b40090e31a76a9b2766a501979f3070fa27f689c27ec04377d32"
+dependencies = [
+ "dyn-stack",
+ "gemm-c32",
+ "gemm-c64",
+ "gemm-common",
+ "gemm-f16",
+ "gemm-f32",
+ "gemm-f64",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-c32"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9c030d0b983d1e34a546b86e08f600c11696fde16199f971cd46c12e67512c0"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-c64"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbb5f2e79fefb9693d18e1066a557b4546cd334b226beadc68b11a8f9431852a"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-common"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2e7ea062c987abcd8db95db917b4ffb4ecdfd0668471d8dc54734fdff2354e8"
+dependencies = [
+ "bytemuck",
+ "dyn-stack",
+ "half",
+ "num-complex",
+ "num-traits",
+ "once_cell",
+ "paste",
+ "pulp",
+ "raw-cpuid",
+ "rayon",
+ "seq-macro",
+ "sysctl",
+]
+
+[[package]]
+name = "gemm-f16"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ca4c06b9b11952071d317604acb332e924e817bd891bec8dfb494168c7cedd4"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "gemm-f32",
+ "half",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "rayon",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-f32"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9a69f51aaefbd9cf12d18faf273d3e982d9d711f60775645ed5c8047b4ae113"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-f64"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa397a48544fadf0b81ec8741e5c0fba0043008113f71f2034def1935645d2b0"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2"
+dependencies = [
+ "gix-hash",
+ "gix-trace",
+ "libc",
+]
+
+[[package]]
+name = "gix-fs"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107"
+dependencies = [
+ "gix-features",
+]
+
+[[package]]
+name = "gix-hash"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0"
+dependencies = [
+ "faster-hex",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-tempfile"
+version = "11.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388dd29114a86ec69b28d1e26d6d63a662300ecf61ab3f4cc578f7d7dc9e7e23"
+dependencies = [
+ "dashmap",
+ "gix-fs",
+ "libc",
+ "once_cell",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-registry",
+ "tempfile",
+]
+
+[[package]]
+name = "gix-trace"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e"
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glow"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+dependencies = [
+ "bitflags 2.5.0",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
+dependencies = [
+ "log",
+ "presser",
+ "thiserror",
+ "winapi",
+ "windows",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
+dependencies = [
+ "bitflags 2.5.0",
+ "gpu-descriptor-types",
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "bytemuck",
+ "cfg-if",
+ "crunchy",
+ "num-traits",
+ "rand",
+ "rand_distr",
+ "serde",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "serde",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
+dependencies = [
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "hassle-rs"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
+dependencies = [
+ "bitflags 2.5.0",
+ "com",
+ "libc",
+ "libloading 0.8.3",
+ "thiserror",
+ "widestring",
+ "winapi",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder",
+ "num-traits",
+ "png",
+ "qoi",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "indoc"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
+dependencies = [
+ "libc",
+ "libloading 0.8.3",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.5.0",
+ "libc",
+]
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "lru"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+dependencies = [
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matrixmultiply"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
+dependencies = [
+ "autocfg",
+ "num_cpus",
+ "once_cell",
+ "rawpointer",
+ "thread-tree",
+]
+
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "memmap2"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
+dependencies = [
+ "libc",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "metal"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
+dependencies = [
+ "bitflags 2.5.0",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
+dependencies = [
+ "adler",
+ "simd-adler32",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "naga"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843"
+dependencies = [
+ "bit-set",
+ "bitflags 2.5.0",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
+name = "ndarray"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "rawpointer",
+ "rayon",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.5.0+25.2.9519653"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "bytemuck",
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "nvml-wrapper"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd21b9f5a1cce3c3515c9ffa85f5c7443e07162dae0ccf4339bb7ca38ad3454"
+dependencies = [
+ "bitflags 1.3.2",
+ "libloading 0.7.4",
+ "nvml-wrapper-sys",
+ "static_assertions",
+ "thiserror",
+ "wrapcenum-derive",
+]
+
+[[package]]
+name = "nvml-wrapper-sys"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c961a2ea9e91c59a69b78e69090f6f5b867bb46c0c56de9482da232437c4987e"
+dependencies = [
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "password-hash"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pbkdf2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
+dependencies = [
+ "digest",
+ "hmac",
+ "password-hash",
+ "sha2",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "png"
+version = "0.17.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "pollster"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "presser"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
+
+[[package]]
+name = "pulp"
+version = "0.18.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "762ad20f6b65f5a33874de3b7506d6cf6631d37fb9c0c6ee0e310d7210a167d9"
+dependencies = [
+ "bytemuck",
+ "libm",
+ "num-complex",
+ "reborrow",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r2d2"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
+dependencies = [
+ "log",
+ "parking_lot",
+ "scheduled-thread-pool",
+]
+
+[[package]]
+name = "r2d2_sqlite"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dc290b669d30e20751e813517bbe13662d020419c5c8818ff10b6e8bb7777f6"
+dependencies = [
+ "r2d2",
+ "rusqlite",
+ "uuid",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
+
+[[package]]
+name = "ratatui"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb"
+dependencies = [
+ "bitflags 2.5.0",
+ "cassowary",
+ "crossterm",
+ "indoc",
+ "itertools",
+ "lru",
+ "paste",
+ "stability",
+ "strum",
+ "time",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "10.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "reborrow"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "renderdoc-sys"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rmp"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
+dependencies = [
+ "byteorder",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
+dependencies = [
+ "byteorder",
+ "rmp",
+ "serde",
+]
+
+[[package]]
+name = "run"
+version = "0.1.0"
+dependencies = [
+ "bincode 1.3.3",
+ "serde",
+ "tank-ffi",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
+dependencies = [
+ "bitflags 2.5.0",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "safetensors"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d93279b86b3de76f820a8854dd06cbc33cfa57a417b19c47f6a25280112fb1df"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "safetensors"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ced76b22c7fba1162f11a5a75d9d8405264b467a07ae0c9c29be119b9297db9"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "sanitize-filename"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "scheduled-thread-pool"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
+dependencies = [
+ "parking_lot",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "seq-macro"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
+
+[[package]]
+name = "serde"
+version = "1.0.202"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.202"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_rusqlite"
+version = "0.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4600dac14aada464c5584d327baa164e372153309bc4c0fb1498bbfbaa5a028b"
+dependencies = [
+ "rusqlite",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spirv"
+version = "0.3.0+sdk-1.3.268.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "stability"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "sysctl"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea"
+dependencies = [
+ "bitflags 2.5.0",
+ "byteorder",
+ "enum-as-inner",
+ "libc",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.30.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "rayon",
+ "windows",
+]
+
+[[package]]
+name = "systemstat"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a24aec24a9312c83999a28e3ef9db7e2afd5c64bf47725b758cdc1cafd5b0bd2"
+dependencies = [
+ "bytesize",
+ "lazy_static",
+ "libc",
+ "nom",
+ "time",
+ "winapi",
+]
+
+[[package]]
+name = "tank-ffi"
+version = "0.1.0"
+dependencies = [
+ "bincode 1.3.3",
+ "burn",
+ "serde",
+]
+
+[[package]]
+name = "tch"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c7cb00bc2770454b515388d45be7097a3ded2eca172f3dcdb7ca4cc06c40bf1"
+dependencies = [
+ "half",
+ "lazy_static",
+ "libc",
+ "ndarray",
+ "rand",
+ "safetensors 0.3.3",
+ "thiserror",
+ "torch-sys",
+ "zip",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "text_placeholder"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512104f982ce6f50def5340f9d7d14cc21f7a859e9ccd251aa19d12e1345c070"
+dependencies = [
+ "hashbrown 0.13.2",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "thread-tree"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbd370cb847953a25954d9f63e14824a36113f8c72eecf6eccef5dc4b45d630"
+dependencies = [
+ "crossbeam-channel",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "torch-sys"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29e0244e5b148a31dd7fe961165037d1927754d024095c1013937532d7e73a22"
+dependencies = [
+ "anyhow",
+ "cc",
+ "libc",
+ "serde",
+ "serde_json",
+ "ureq",
+ "zip",
+]
+
+[[package]]
+name = "tracing-appender"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
+dependencies = [
+ "crossbeam-channel",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "train"
+version = "0.1.0"
+dependencies = [
+ "bincode 1.3.3",
+ "burn",
+ "serde",
+ "tank-ffi",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "ureq"
+version = "2.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
+dependencies = [
+ "base64",
+ "flate2",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "serde",
+ "serde_json",
+ "url",
+ "webpki-roots",
+]
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "uuid"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+dependencies = [
+ "getrandom",
+ "rand",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
+[[package]]
+name = "wgpu"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
+dependencies = [
+ "arrayvec",
+ "cfg-if",
+ "cfg_aliases",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags 2.5.0",
+ "cfg_aliases",
+ "codespan-reporting",
+ "indexmap",
+ "log",
+ "naga",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1a4924366df7ab41a5d8546d6534f1f33231aa5b3f72b9930e300f254e39c3"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags 2.5.0",
+ "block",
+ "cfg_aliases",
+ "core-graphics-types",
+ "d3d12",
+ "glow",
+ "glutin_wgl_sys",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "hassle-rs",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading 0.8.3",
+ "log",
+ "metal",
+ "naga",
+ "ndk-sys",
+ "objc",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805"
+dependencies = [
+ "bitflags 2.5.0",
+ "js-sys",
+ "web-sys",
+]
+
+[[package]]
+name = "widestring"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
+[[package]]
+name = "wrapcenum-derive"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
+
+[[package]]
+name = "yoke"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
+[[package]]
+name = "zip"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
+dependencies = [
+ "aes",
+ "byteorder",
+ "bzip2",
+ "constant_time_eq",
+ "crc32fast",
+ "crossbeam-utils",
+ "flate2",
+ "hmac",
+ "pbkdf2",
+ "sha1",
+ "time",
+ "zstd",
+]
+
+[[package]]
+name = "zstd"
+version = "0.11.2+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "5.0.2+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.10+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
diff --git a/TankMan/.gitignore b/TankMan/.gitignore
new file mode 100644
index 0000000..44adb93
--- /dev/null
+++ b/TankMan/.gitignore
@@ -0,0 +1,165 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+.idea/
+
+asset/image/mapping/image.pptx
+
+ml/save/
+log/
\ No newline at end of file
diff --git a/TankMan/Mapping.md b/TankMan/Mapping.md
new file mode 100644
index 0000000..76fa648
--- /dev/null
+++ b/TankMan/Mapping.md
@@ -0,0 +1,170 @@
+# 地圖製作教學
+
+此文將會介紹如何使用`Tiled`這個軟體製作一個地圖,並放進[PAIA坦克大作戰](https://github.com/Jesse-Jumbo/TankMan) 中,讓我們的坦克車可以跑在自己客製化的地圖。
+
+建立好的地圖除了可以直接放入[PAIA坦克大作戰](https://github.com/Jesse-Jumbo/TankMan) ,在個人環境中做使用之外,也可以分享給其他人,讓所有人使用。
+
+---
+# 檔案更新
+
+- [圖塊集](https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/TankManObj.png)
+---
+
+# 軟體安裝
+ ⚙ 製作地圖需要安裝Tiled這個軟體,請大家到 [官網](https://www.mapeditor.org) 中下載適合自己電腦作業系統的版本:
+
+## 安裝流程:
+
+1. 進入到[官網](https://www.mapeditor.org)後點擊「Donload on itch.io」跳轉到下載頁面。
+ 
+
+
+
+2. 點擊「Download Now」後將會顯示捐款浮動視窗。
+ 
+
+
+3. 點選最上方「No thanks, just take me to the downloads」的選項後,即可看到不同作業系統適用的安裝檔。
+ 
+
+
+4. 選擇與自己電腦相容的安裝檔即可下載並安裝。
+ 
+
+
+
+---
+
+# 新增地圖專案
+ ⚙ 這部分主要說明如何建立一個地圖檔案並儲存。
+
+
+
+1. 開啟Tiled,建立新地圖
+第一次開啟Tiled的時候,可以在左上角看到以下截圖畫面。其中紅框內是比較常用到的功能。
+選擇「New Map」,進入到軟體中。
+ 
+
+
+
+---
+
+1. 設定地圖大小
+
+ 選擇建立新地圖後會跳出地圖屬性設定視窗,其中「地圖大小」的設定將會直接影響迷宮大小。
+
+ 將「圖塊大小」的高與寬都調整回50px,並設定好地圖大小之後就可以按下「Save As」
+ 
+
+
+
+---
+
+1. 儲存檔案
+
+ 存檔時需要注意存檔類型,預設為`.tmx`,也是Tiled可以直接打開編輯的檔案格式;而坦克車使用的也是TMX檔。
+
+ 
+
+
+---
+
+1. 新增圖塊集
+
+ 存好檔案之後就會進入到編輯頁面。
+
+ - 點選圖塊區域中間的「New Tileset」
+
+ 
+
+
+- 再點擊「Browser」並打開[圖片檔](https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/TankManObj.png) 。
+ 
+
+
+- 將圖片寬度與高度調整為50px,邊距與間距分別為0px與0px。
+ 
+
+
+- 成功新增圖庫集之後就可以開始編輯地圖了
+ 
+
+
+---
+
+# 編輯地圖
+
+
+ ⚙ Tiled的基本編輯方式相當簡單,只需要使用滑鼠選取圖塊集的方塊,再移動到編輯區點擊方格即可。
+ 也可以持續按著滑鼠左鍵拖曳繪製
+
+
+---
+## 積木種類
+
+圖塊集的每個格子都會有相對應的編號,其順序從1開始,由左至右、由上至下。
+
+目前坦克車使用到的編號為1~5,對應到的圖塊如圖所示,這5種圖塊的功能分別為:
+
+1. 綠色坦克,玩家1P的起始位置,面向西方。
+2. 藍色坦克,玩家2P的起始位置,面向東方。
+3. 磚牆,遊戲中的牆壁,被擊中3次會消失。
+4. 子彈圖示,子彈補給站的起始位置,每次經過可獲得5顆子彈。
+5. WiFi圖示,油料補給站的起始位置,每次經過可獲得30點油料。
+
+
+
+
+---
+
+## 地圖圖塊與遊戲物件的關係
+
+- 地圖圖塊的大小 = 遊戲物件的大小。
+
+---
+
+## 常用工具與功能列
+
+- 橡皮擦
+
+ 在上方工具列可以找到,使用方式與圖塊集相同。
+
+ 
+
+
+- 調整地圖大小
+
+ 在上方的選單選擇「地圖」→「調整地圖大小」
+
+ 可以調整當前地圖的長寬,並移動原先的地圖物件,在視窗中間將會出現預覽圖。
+ 
+
+
+---
+
+# FAQ 與 注意事項
+
+## 地圖存放路徑
+```python
+Game/asset/maps/
+```
+
+
+## 地圖檔名命名規則
+
+- 檔名最後的數字(`x`)就是地圖的編號。
+ - 地圖格式:map_0`x`.tmx
+
+---
+
+## 如何更換新版圖塊集
+當更新了新版圖塊集。過去有使用過舊版的使用者有可能遇到編號不相容的問題,在此提供解決方法。
+
+1. 避免在同一個檔案下使用兩個圖塊集
+2. 刪除舊版圖塊集
+
+
+
+當軟體中存在一個以上的圖塊集時編號將會存在衝突,導致載入遊戲時無法順利產生物件,因此建議使用者在同一個地圖檔案內不要使用兩個圖塊集。例如:在曾經以舊版編輯過的地圖裡直接使用新版圖塊集,可以選擇重新開新的地圖檔案套用新版的圖塊集。如果還是擔心會有混用的問題,在圖塊集的下方有垃圾桶的Icon,可以直接刪除舊的圖塊集。
+
+---
\ No newline at end of file
diff --git a/TankMan/README.md b/TankMan/README.md
new file mode 100644
index 0000000..d41d737
--- /dev/null
+++ b/TankMan/README.md
@@ -0,0 +1,129 @@
+# TankMan
+## 遊戲說明
+
+
+
+[](https://github.com/Jesse-Jumbo/TankMan/tree/0.7.0)
+[](https://www.python.org/downloads/release/python-390/)
+[](https://pypi.org/project/mlgame/10.2.5a0/)
+[](https://github.com/pygame/pygame/releases/tag/2.0.1)
+
+
+坦克人(Tank Man),一款經典的雙人對戰遊戲,時間內率先擊殺對手獲勝,否則以分數高者獲勝,除了擊中對手外,破壞遊戲物件,以獲得更高積分。
+
+!注意: 場上資源恢復皆須時間,先到者得,你需要這些補充品以提供前進和射擊的燃油和子彈。
+
+
+---
+## Requirements
+- Python==3.9
+- mlgame==10.2.5a0
+- pytmx=3.31
+---
+## 更新說明
+- 0.6.0 版本之後,遊戲為 2~6人 團隊對抗遊戲
+- 0.7.0 版本之後,坦克和砲管的方向可以分開控制
+---
+## 遊戲簡介:
+2~6位玩家進行團隊對抗賽,GreenTeam 為綠色坦克車,BlueTeam 為藍色坦克車,透過回傳遊戲指令,操控玩家與射擊砲彈,場上會有各類補給站,經過以補給該資源。
+
+---
+## 畫面說明(2.x版本):
+
+
+---
+# 遊戲細節:
+## 啟動方式:
+- 在命令行輸入命令執行。
+---
+## 遊戲參數設定
+- 在以下命令中,`.`代表執行的遊戲專案路徑,若`.`後不輸入參數,則默認使用預設值,即`game_config.json`內參數值。
+```bash
+# MLGame.py
+# Copy and Paste to play battle game with manual
+python -m mlgame -f 120 -i ml/ml_play_manual.py -i ml/ml_play_manual.py . --green_team_num 1 --blue_team_num 1 --is_manual 1 --frame_limit 1000
+
+# Copy and Paste to play battle game with AI
+python -m mlgame -f 120 -i ml/ml_play.py -i ml/ml_play.py . --green_team_num 1 --blue_team_num 1 --frame_limit 1000
+```
+- `green_team_num`:選擇數字 1~3,以決定 綠隊 人數。
+- `blue_team_num`:選擇數字 1~3,以決定 藍隊 人數。
+- `is_manual`: 輸入 "1" 啟用手動模式,以讓遊戲適合手動遊玩。
+- `frame_limit`: 輸入數字 30~3000,以決定遊戲的總幀數。
+- `sound`: 輸入`on`或`off`,控制是否播放遊戲音效。
+- 如果在`mlgame`後加上`-1`,代表只執行一次遊戲。
+---
+## 遊戲操作:
+
+### 使用鍵盤
+- 角色移動:方向鍵控制 1P,WASD 鍵控制 2P 的移動和轉彎。
+- 角色射擊:1P 按下`M`鍵進行射擊,2P 按下`F`鍵進行射擊。
+- 遊戲畫面: 透過`I、K、J、L`來上下左右移動畫面;透過`O、U`來放大縮小畫面。
+- 隱藏遊戲資訊:按下`H`鍵,可隱藏畫面中的遊戲資訊。
+- 遊戲暫停:按下`P`鍵,可暫停遊戲(mlgame 10.2 後版本才有效)。
+- 遊戲暫停:遊戲 AI 回傳`PAUSED`指令,可暫停遊戲。
+
+
+### AI控制
+- 藉由遊戲資訊,在`ml`資料夾,撰寫控制遊戲角色的`.py`檔。
+- 可參考`ml_play.py`自動範例程式,撰寫自動遊玩的程式。
+- 可參考`ml_play_manual.py`手動範例程式,撰寫手動遊玩的程式。
+---
+# 遊戲玩法
+1. 團隊對抗戰 → 時間內殲滅敵對,或高分隊伍獲勝。
+## 過關條件
+1. 團隊對抗戰
+ 1. 將敵隊全數擊敗。
+ 2. 高過敵隊積分:
+ - 對方失去的生命 * `20`分。
+ - 每擊中一次牆壁 * `1`分。
+ - 擊破牆壁 * `5`分。
+---
+## 失敗條件
+
+1. 團隊對抗戰
+ 1. 生命歸零。
+ 2. 時間結束,分數較敵隊低。
+---
+## 物件設定:
+### **`Tank`**
+
+---
+1. 前進、後退速度(8 px)
+2. 轉彎角度(45度)
+3. 生命機會(3次)
+4. 燃油(100)
+5. 彈匣(10)
+---
+### **`Walls`**
+1. 生命次數(4)
+2. 透明設定(依照生命次數決定)
+---
+### **`補給站`**
+1. 燃油站
+ - 玩家經過補充 30點燃油,超過 100,則無效。
+ - 與玩家碰撞,則隨機換位置。
+
+2. 彈藥站
+ - 玩家經過補充 5顆彈藥,超過 10,則無效。
+ - 與玩家碰撞,則隨機換位置。
+
+---
+# 地圖說明
+- 寬1000 pixel;高600 pixel
+- 每格50 * 50 pixel,可放置一個物件
+
+---
+# 地圖製作
+- 地圖製作教學 [Mapping.md](Mapping.md)
+
+---
+# image sours
+- [Green Tank/Blue Tank](https://linevoom.line.me/user/_dV001P0rSN_bh8zGE0q4jmdr4Fn5d-j73cLrjTc?utm_medium=windows&utm_source=desktop&utm_campaign=Profile)
+- [Bullet](https://linevoom.line.me/user/_dV001P0rSN_bh8zGE0q4jmdr4Fn5d-j73cLrjTc?utm_medium=windows&utm_source=desktop&utm_campaign=Profile)
+- [Hourglass](https://opengameart.org/content/animated-hourglass)
+- [Other Object](https://opengameart.org/content/simple-shooter-icons)
+
+# sound sours
+- [BGM](https://opengameart.org/content/commando-team-action-loop-cut)
+- [SHOOT](https://opengameart.org/content/random-low-quality-sfx)
diff --git a/TankMan/__init__.py b/TankMan/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/TankMan/asset/image/1P.png b/TankMan/asset/image/1P.png
new file mode 100644
index 0000000..cdceba7
Binary files /dev/null and b/TankMan/asset/image/1P.png differ
diff --git a/TankMan/asset/image/1P.svg b/TankMan/asset/image/1P.svg
new file mode 100644
index 0000000..249ef50
--- /dev/null
+++ b/TankMan/asset/image/1P.svg
@@ -0,0 +1,259 @@
+
+
+
+
diff --git a/TankMan/asset/image/1P_body.png b/TankMan/asset/image/1P_body.png
new file mode 100644
index 0000000..4605a68
Binary files /dev/null and b/TankMan/asset/image/1P_body.png differ
diff --git a/TankMan/asset/image/1P_body.svg b/TankMan/asset/image/1P_body.svg
new file mode 100644
index 0000000..9182ca7
--- /dev/null
+++ b/TankMan/asset/image/1P_body.svg
@@ -0,0 +1,150 @@
+
+
diff --git a/TankMan/asset/image/1P_gun.png b/TankMan/asset/image/1P_gun.png
new file mode 100644
index 0000000..bbf83ae
Binary files /dev/null and b/TankMan/asset/image/1P_gun.png differ
diff --git a/TankMan/asset/image/1P_gun.svg b/TankMan/asset/image/1P_gun.svg
new file mode 100644
index 0000000..ecedcc5
--- /dev/null
+++ b/TankMan/asset/image/1P_gun.svg
@@ -0,0 +1,96 @@
+
+
diff --git a/TankMan/asset/image/2P.png b/TankMan/asset/image/2P.png
new file mode 100644
index 0000000..d24571c
Binary files /dev/null and b/TankMan/asset/image/2P.png differ
diff --git a/TankMan/asset/image/2P.svg b/TankMan/asset/image/2P.svg
new file mode 100644
index 0000000..4cdddcd
--- /dev/null
+++ b/TankMan/asset/image/2P.svg
@@ -0,0 +1,262 @@
+
+
+
+
diff --git a/TankMan/asset/image/2P_body.png b/TankMan/asset/image/2P_body.png
new file mode 100644
index 0000000..26b978a
Binary files /dev/null and b/TankMan/asset/image/2P_body.png differ
diff --git a/TankMan/asset/image/2P_body.svg b/TankMan/asset/image/2P_body.svg
new file mode 100644
index 0000000..e732987
--- /dev/null
+++ b/TankMan/asset/image/2P_body.svg
@@ -0,0 +1,150 @@
+
+
diff --git a/TankMan/asset/image/2P_gun.png b/TankMan/asset/image/2P_gun.png
new file mode 100644
index 0000000..c0c1ad2
Binary files /dev/null and b/TankMan/asset/image/2P_gun.png differ
diff --git a/TankMan/asset/image/2P_gun.svg b/TankMan/asset/image/2P_gun.svg
new file mode 100644
index 0000000..696327f
--- /dev/null
+++ b/TankMan/asset/image/2P_gun.svg
@@ -0,0 +1,96 @@
+
+
diff --git a/TankMan/asset/image/TankMan.svg b/TankMan/asset/image/TankMan.svg
new file mode 100644
index 0000000..27e3854
--- /dev/null
+++ b/TankMan/asset/image/TankMan.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/TankMan/asset/image/TankManObj.png b/TankMan/asset/image/TankManObj.png
new file mode 100644
index 0000000..945a93a
Binary files /dev/null and b/TankMan/asset/image/TankManObj.png differ
diff --git a/TankMan/asset/image/TankMan_obj.svg b/TankMan/asset/image/TankMan_obj.svg
new file mode 100644
index 0000000..446ce99
--- /dev/null
+++ b/TankMan/asset/image/TankMan_obj.svg
@@ -0,0 +1,1184 @@
+
+
+
+
diff --git a/TankMan/asset/image/border.png b/TankMan/asset/image/border.png
new file mode 100644
index 0000000..fe91691
Binary files /dev/null and b/TankMan/asset/image/border.png differ
diff --git a/TankMan/asset/image/border.svg b/TankMan/asset/image/border.svg
new file mode 100644
index 0000000..92252a2
--- /dev/null
+++ b/TankMan/asset/image/border.svg
@@ -0,0 +1,164 @@
+
+
+
+
diff --git a/TankMan/asset/image/bullets.png b/TankMan/asset/image/bullets.png
new file mode 100644
index 0000000..b49e98d
Binary files /dev/null and b/TankMan/asset/image/bullets.png differ
diff --git a/TankMan/asset/image/bullets.svg b/TankMan/asset/image/bullets.svg
new file mode 100644
index 0000000..bb03710
--- /dev/null
+++ b/TankMan/asset/image/bullets.svg
@@ -0,0 +1,201 @@
+
+
+
+
diff --git a/TankMan/asset/image/game.gif b/TankMan/asset/image/game.gif
new file mode 100644
index 0000000..7f9dd5c
Binary files /dev/null and b/TankMan/asset/image/game.gif differ
diff --git a/TankMan/asset/image/grass_0.png b/TankMan/asset/image/grass_0.png
new file mode 100644
index 0000000..a1b03e4
Binary files /dev/null and b/TankMan/asset/image/grass_0.png differ
diff --git a/TankMan/asset/image/grass_1.png b/TankMan/asset/image/grass_1.png
new file mode 100644
index 0000000..91ce067
Binary files /dev/null and b/TankMan/asset/image/grass_1.png differ
diff --git a/TankMan/asset/image/grass_2.png b/TankMan/asset/image/grass_2.png
new file mode 100644
index 0000000..bbcbbdd
Binary files /dev/null and b/TankMan/asset/image/grass_2.png differ
diff --git a/TankMan/asset/image/hourglass.png b/TankMan/asset/image/hourglass.png
new file mode 100644
index 0000000..8dde1e1
Binary files /dev/null and b/TankMan/asset/image/hourglass.png differ
diff --git a/TankMan/asset/image/hourglass_0.png b/TankMan/asset/image/hourglass_0.png
new file mode 100644
index 0000000..11ddf59
Binary files /dev/null and b/TankMan/asset/image/hourglass_0.png differ
diff --git a/TankMan/asset/image/hourglass_1.png b/TankMan/asset/image/hourglass_1.png
new file mode 100644
index 0000000..049a86c
Binary files /dev/null and b/TankMan/asset/image/hourglass_1.png differ
diff --git a/TankMan/asset/image/hourglass_10.png b/TankMan/asset/image/hourglass_10.png
new file mode 100644
index 0000000..1990d7c
Binary files /dev/null and b/TankMan/asset/image/hourglass_10.png differ
diff --git a/TankMan/asset/image/hourglass_11.png b/TankMan/asset/image/hourglass_11.png
new file mode 100644
index 0000000..d5a3427
Binary files /dev/null and b/TankMan/asset/image/hourglass_11.png differ
diff --git a/TankMan/asset/image/hourglass_12.png b/TankMan/asset/image/hourglass_12.png
new file mode 100644
index 0000000..a8c5904
Binary files /dev/null and b/TankMan/asset/image/hourglass_12.png differ
diff --git a/TankMan/asset/image/hourglass_13.png b/TankMan/asset/image/hourglass_13.png
new file mode 100644
index 0000000..0eca37d
Binary files /dev/null and b/TankMan/asset/image/hourglass_13.png differ
diff --git a/TankMan/asset/image/hourglass_14.png b/TankMan/asset/image/hourglass_14.png
new file mode 100644
index 0000000..b63624f
Binary files /dev/null and b/TankMan/asset/image/hourglass_14.png differ
diff --git a/TankMan/asset/image/hourglass_2.png b/TankMan/asset/image/hourglass_2.png
new file mode 100644
index 0000000..8f80097
Binary files /dev/null and b/TankMan/asset/image/hourglass_2.png differ
diff --git a/TankMan/asset/image/hourglass_3.png b/TankMan/asset/image/hourglass_3.png
new file mode 100644
index 0000000..172e6db
Binary files /dev/null and b/TankMan/asset/image/hourglass_3.png differ
diff --git a/TankMan/asset/image/hourglass_4.png b/TankMan/asset/image/hourglass_4.png
new file mode 100644
index 0000000..ae265fa
Binary files /dev/null and b/TankMan/asset/image/hourglass_4.png differ
diff --git a/TankMan/asset/image/hourglass_5.png b/TankMan/asset/image/hourglass_5.png
new file mode 100644
index 0000000..ff29339
Binary files /dev/null and b/TankMan/asset/image/hourglass_5.png differ
diff --git a/TankMan/asset/image/hourglass_6.png b/TankMan/asset/image/hourglass_6.png
new file mode 100644
index 0000000..c978d37
Binary files /dev/null and b/TankMan/asset/image/hourglass_6.png differ
diff --git a/TankMan/asset/image/hourglass_7.png b/TankMan/asset/image/hourglass_7.png
new file mode 100644
index 0000000..3902a87
Binary files /dev/null and b/TankMan/asset/image/hourglass_7.png differ
diff --git a/TankMan/asset/image/hourglass_8.png b/TankMan/asset/image/hourglass_8.png
new file mode 100644
index 0000000..76b9dfb
Binary files /dev/null and b/TankMan/asset/image/hourglass_8.png differ
diff --git a/TankMan/asset/image/hourglass_9.png b/TankMan/asset/image/hourglass_9.png
new file mode 100644
index 0000000..b72e8cc
Binary files /dev/null and b/TankMan/asset/image/hourglass_9.png differ
diff --git a/TankMan/asset/image/logo.png b/TankMan/asset/image/logo.png
new file mode 100644
index 0000000..8d707f3
Binary files /dev/null and b/TankMan/asset/image/logo.png differ
diff --git a/TankMan/asset/image/mapping/define_map_format.jpg b/TankMan/asset/image/mapping/define_map_format.jpg
new file mode 100644
index 0000000..4b6f631
Binary files /dev/null and b/TankMan/asset/image/mapping/define_map_format.jpg differ
diff --git a/TankMan/asset/image/mapping/del_image.png b/TankMan/asset/image/mapping/del_image.png
new file mode 100644
index 0000000..6e05348
Binary files /dev/null and b/TankMan/asset/image/mapping/del_image.png differ
diff --git a/TankMan/asset/image/mapping/image_no.png b/TankMan/asset/image/mapping/image_no.png
new file mode 100644
index 0000000..b48e40e
Binary files /dev/null and b/TankMan/asset/image/mapping/image_no.png differ
diff --git a/TankMan/asset/image/mapping/image_path.png b/TankMan/asset/image/mapping/image_path.png
new file mode 100644
index 0000000..e2798e3
Binary files /dev/null and b/TankMan/asset/image/mapping/image_path.png differ
diff --git a/TankMan/asset/image/mapping/map.png b/TankMan/asset/image/mapping/map.png
new file mode 100644
index 0000000..0139a7b
Binary files /dev/null and b/TankMan/asset/image/mapping/map.png differ
diff --git a/TankMan/asset/image/mapping/new_tilsests.png b/TankMan/asset/image/mapping/new_tilsests.png
new file mode 100644
index 0000000..6e99f2d
Binary files /dev/null and b/TankMan/asset/image/mapping/new_tilsests.png differ
diff --git a/TankMan/asset/image/mapping/object_image.png b/TankMan/asset/image/mapping/object_image.png
new file mode 100644
index 0000000..ec58eb8
Binary files /dev/null and b/TankMan/asset/image/mapping/object_image.png differ
diff --git a/TankMan/asset/image/mapping/save_file.jpg b/TankMan/asset/image/mapping/save_file.jpg
new file mode 100644
index 0000000..425ae6f
Binary files /dev/null and b/TankMan/asset/image/mapping/save_file.jpg differ
diff --git a/TankMan/asset/image/mapping/tilsest_format.png b/TankMan/asset/image/mapping/tilsest_format.png
new file mode 100644
index 0000000..190fed0
Binary files /dev/null and b/TankMan/asset/image/mapping/tilsest_format.png differ
diff --git a/TankMan/asset/image/not_used.svg b/TankMan/asset/image/not_used.svg
new file mode 100644
index 0000000..2f99724
--- /dev/null
+++ b/TankMan/asset/image/not_used.svg
@@ -0,0 +1,185 @@
+
+
+
+
diff --git a/TankMan/asset/image/oil.png b/TankMan/asset/image/oil.png
new file mode 100644
index 0000000..95a3563
Binary files /dev/null and b/TankMan/asset/image/oil.png differ
diff --git a/TankMan/asset/image/oil.svg b/TankMan/asset/image/oil.svg
new file mode 100644
index 0000000..fbf0623
--- /dev/null
+++ b/TankMan/asset/image/oil.svg
@@ -0,0 +1,227 @@
+
+
+
+
diff --git a/TankMan/asset/image/qubodup_ringicons.svg b/TankMan/asset/image/qubodup_ringicons.svg
new file mode 100644
index 0000000..6cba1d1
--- /dev/null
+++ b/TankMan/asset/image/qubodup_ringicons.svg
@@ -0,0 +1,157 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_a_bullet.png b/TankMan/asset/image/team_a_bullet.png
new file mode 100644
index 0000000..a61a7fd
Binary files /dev/null and b/TankMan/asset/image/team_a_bullet.png differ
diff --git a/TankMan/asset/image/team_a_bullet.svg b/TankMan/asset/image/team_a_bullet.svg
new file mode 100644
index 0000000..8100912
--- /dev/null
+++ b/TankMan/asset/image/team_a_bullet.svg
@@ -0,0 +1,198 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_a_lives_1.png b/TankMan/asset/image/team_a_lives_1.png
new file mode 100644
index 0000000..87b97e5
Binary files /dev/null and b/TankMan/asset/image/team_a_lives_1.png differ
diff --git a/TankMan/asset/image/team_a_lives_1.svg b/TankMan/asset/image/team_a_lives_1.svg
new file mode 100644
index 0000000..e7a4e23
--- /dev/null
+++ b/TankMan/asset/image/team_a_lives_1.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_a_lives_2.png b/TankMan/asset/image/team_a_lives_2.png
new file mode 100644
index 0000000..4ef3e30
Binary files /dev/null and b/TankMan/asset/image/team_a_lives_2.png differ
diff --git a/TankMan/asset/image/team_a_lives_2.svg b/TankMan/asset/image/team_a_lives_2.svg
new file mode 100644
index 0000000..e1bc6cc
--- /dev/null
+++ b/TankMan/asset/image/team_a_lives_2.svg
@@ -0,0 +1,169 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_a_lives_3.png b/TankMan/asset/image/team_a_lives_3.png
new file mode 100644
index 0000000..2155ecc
Binary files /dev/null and b/TankMan/asset/image/team_a_lives_3.png differ
diff --git a/TankMan/asset/image/team_a_lives_3.svg b/TankMan/asset/image/team_a_lives_3.svg
new file mode 100644
index 0000000..d7ca9ca
--- /dev/null
+++ b/TankMan/asset/image/team_a_lives_3.svg
@@ -0,0 +1,169 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_b_bullet.png b/TankMan/asset/image/team_b_bullet.png
new file mode 100644
index 0000000..fdd0970
Binary files /dev/null and b/TankMan/asset/image/team_b_bullet.png differ
diff --git a/TankMan/asset/image/team_b_bullet.svg b/TankMan/asset/image/team_b_bullet.svg
new file mode 100644
index 0000000..1e0fe61
--- /dev/null
+++ b/TankMan/asset/image/team_b_bullet.svg
@@ -0,0 +1,195 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_b_lives_1.png b/TankMan/asset/image/team_b_lives_1.png
new file mode 100644
index 0000000..021743e
Binary files /dev/null and b/TankMan/asset/image/team_b_lives_1.png differ
diff --git a/TankMan/asset/image/team_b_lives_1.svg b/TankMan/asset/image/team_b_lives_1.svg
new file mode 100644
index 0000000..9cad6d9
--- /dev/null
+++ b/TankMan/asset/image/team_b_lives_1.svg
@@ -0,0 +1,169 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_b_lives_2.png b/TankMan/asset/image/team_b_lives_2.png
new file mode 100644
index 0000000..db1aa9f
Binary files /dev/null and b/TankMan/asset/image/team_b_lives_2.png differ
diff --git a/TankMan/asset/image/team_b_lives_2.svg b/TankMan/asset/image/team_b_lives_2.svg
new file mode 100644
index 0000000..b6addbb
--- /dev/null
+++ b/TankMan/asset/image/team_b_lives_2.svg
@@ -0,0 +1,169 @@
+
+
+
+
diff --git a/TankMan/asset/image/team_b_lives_3.png b/TankMan/asset/image/team_b_lives_3.png
new file mode 100644
index 0000000..88ec74b
Binary files /dev/null and b/TankMan/asset/image/team_b_lives_3.png differ
diff --git a/TankMan/asset/image/team_b_lives_3.svg b/TankMan/asset/image/team_b_lives_3.svg
new file mode 100644
index 0000000..0a011c2
--- /dev/null
+++ b/TankMan/asset/image/team_b_lives_3.svg
@@ -0,0 +1,161 @@
+
+
+
+
diff --git a/TankMan/asset/image/view_ex.png b/TankMan/asset/image/view_ex.png
new file mode 100644
index 0000000..945f3cc
Binary files /dev/null and b/TankMan/asset/image/view_ex.png differ
diff --git a/TankMan/asset/image/wall.png b/TankMan/asset/image/wall.png
new file mode 100644
index 0000000..0eba8d3
Binary files /dev/null and b/TankMan/asset/image/wall.png differ
diff --git a/TankMan/asset/image/wall_1.png b/TankMan/asset/image/wall_1.png
new file mode 100644
index 0000000..2c2b250
Binary files /dev/null and b/TankMan/asset/image/wall_1.png differ
diff --git a/TankMan/asset/image/wall_2.png b/TankMan/asset/image/wall_2.png
new file mode 100644
index 0000000..c9a2e41
Binary files /dev/null and b/TankMan/asset/image/wall_2.png differ
diff --git a/TankMan/asset/image/wall_3.png b/TankMan/asset/image/wall_3.png
new file mode 100644
index 0000000..0eba8d3
Binary files /dev/null and b/TankMan/asset/image/wall_3.png differ
diff --git a/TankMan/asset/maps/map_1_v_1.tmx b/TankMan/asset/maps/map_1_v_1.tmx
new file mode 100644
index 0000000..0bf0636
--- /dev/null
+++ b/TankMan/asset/maps/map_1_v_1.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_1_v_2.tmx b/TankMan/asset/maps/map_1_v_2.tmx
new file mode 100644
index 0000000..83e3162
--- /dev/null
+++ b/TankMan/asset/maps/map_1_v_2.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_1_v_5.tmx b/TankMan/asset/maps/map_1_v_5.tmx
new file mode 100644
index 0000000..fc830ae
--- /dev/null
+++ b/TankMan/asset/maps/map_1_v_5.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_2_v_1.tmx b/TankMan/asset/maps/map_2_v_1.tmx
new file mode 100644
index 0000000..b7101b0
--- /dev/null
+++ b/TankMan/asset/maps/map_2_v_1.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_2_v_2.tmx b/TankMan/asset/maps/map_2_v_2.tmx
new file mode 100644
index 0000000..ce030b2
--- /dev/null
+++ b/TankMan/asset/maps/map_2_v_2.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_2_v_3.tmx b/TankMan/asset/maps/map_2_v_3.tmx
new file mode 100644
index 0000000..dba563b
--- /dev/null
+++ b/TankMan/asset/maps/map_2_v_3.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_3_v_2.tmx b/TankMan/asset/maps/map_3_v_2.tmx
new file mode 100644
index 0000000..3fe8591
--- /dev/null
+++ b/TankMan/asset/maps/map_3_v_2.tmx
@@ -0,0 +1,22 @@
+
+
diff --git a/TankMan/asset/maps/map_3_v_3.tmx b/TankMan/asset/maps/map_3_v_3.tmx
new file mode 100644
index 0000000..5ec4a1b
--- /dev/null
+++ b/TankMan/asset/maps/map_3_v_3.tmx
@@ -0,0 +1,35 @@
+
+
+
\ No newline at end of file
diff --git a/TankMan/asset/sound/BGM.ogg b/TankMan/asset/sound/BGM.ogg
new file mode 100644
index 0000000..a42d843
Binary files /dev/null and b/TankMan/asset/sound/BGM.ogg differ
diff --git a/TankMan/asset/sound/shoot.wav b/TankMan/asset/sound/shoot.wav
new file mode 100644
index 0000000..84fd6c0
Binary files /dev/null and b/TankMan/asset/sound/shoot.wav differ
diff --git a/TankMan/asset/sound/touch.wav b/TankMan/asset/sound/touch.wav
new file mode 100644
index 0000000..a720814
Binary files /dev/null and b/TankMan/asset/sound/touch.wav differ
diff --git a/TankMan/blockly.json b/TankMan/blockly.json
new file mode 100644
index 0000000..2dda426
--- /dev/null
+++ b/TankMan/blockly.json
@@ -0,0 +1,61 @@
+{
+ "INIT_INFO": [
+ ["ai_name", "player number", "玩家編號"]
+ ],
+ "PLAYER_STATUS":[
+ ["1P", "1P", "1P"],
+ ["2P", "2P", "2P"],
+ ["3P", "3P", "3P"],
+ ["4P", "4P", "4P"],
+ ["5P", "5P", "5P"],
+ ["6P", "6P", "6P"]
+ ],
+ "GAME_STATUS": [
+ ["GAME_ALIVE", "alive", "存活"],
+ ["GAME_DRAW", "draw", "平手"],
+ ["GREEN_TEAM_WIN", "green team win", "綠隊獲勝"],
+ ["BLUE_TEAM_WIN", "blue team win", "籃隊獲勝"]
+ ],
+ "SCENE_INFO": [
+ ["scene_info['id']", "player number", "玩家編號"],
+ ["scene_info['x']", "x coordinate of player", "玩家的 x 座標"],
+ ["scene_info['y']", "y coordinate of player", "玩家的 y 座標"],
+ ["scene_info['speed']", "player speed", "玩家速度"],
+ ["scene_info['score']", "score of player", "玩家分數"],
+ ["scene_info['power']", "power of player", "玩家子彈數"],
+ ["scene_info['oil']", "oil of player", "玩家油量"],
+ ["scene_info['lives']", "lives of player", "玩家的生命次數"],
+ ["scene_info['angle']", "angle of player", "玩家的面朝角度"],
+ ["scene_info['used_frame']", "# frame", "# 幀數"],
+ ["scene_info['status']", "game status", "遊戲狀態"],
+ ["scene_info['competitor_info']", "coordinate of all competitor", "所有對手的所有資訊"],
+ ["scene_info['walls_info']", "coordinate of all walls", "所有牆的所有資訊"],
+ ["scene_info['bullet_stations_info']", "information of all walls", "所有彈藥庫的所有資訊"],
+ ["scene_info['oil_stations_info']", "information of all walls", "所有加油站的所有資訊"]
+ ],
+ "CONSTANT": [
+ [0, "left boundary", "左邊界"],
+ [1000, "right boundary", "右邊界"],
+ [0, "top boundary", "上邊界"],
+ [600, "bottom boundary", "下邊界"],
+ [50, "obj width", "物件寬度"],
+ [50, "obj height", "物件高度"]
+ ],
+ "ACTION": [
+ ["['FORWARD']", "forward", "前進"],
+ ["['BACKWARD']", "backward", "後退"],
+ ["['TURN_LEFT']", "turn left", "左轉"],
+ ["['TURN_RIGHT']", "turn right", "右轉"],
+ ["['AIM_LEFT']", "aim left", "砲管左轉"],
+ ["['AIM_RIGHT']", "aim right", "砲管右轉"],
+ ["['SHOOT']","shoot", "射擊"],
+ ["['FORWARD', 'SHOOT']", "forward shoot", "前進並射擊"],
+ ["['BACKWARD', 'SHOOT']", "backward shoot", "後退並射擊"],
+ ["['TURN_LEFT', 'SHOOT']", "turn left shoot", "左轉並射擊"],
+ ["['TURN_RIGHT', 'SHOOT']", "turn right shoot", "右轉並射擊"],
+ ["['TURN_LEFT', 'AIM_LEFT', 'SHOOT']", "turn left and aim left shoot", "左轉並砲管左轉射擊"],
+ ["['TURN_RIGHT', 'AIM_RIGHT', 'SHOOT']", "turn right and aim right shoot", "右轉並砲管右轉射擊"],
+ ["['NONE']","doing nothing", "不動作"],
+ ["RESET", "reset", "重置遊戲"]
+ ]
+}
\ No newline at end of file
diff --git a/TankMan/command.txt b/TankMan/command.txt
new file mode 100644
index 0000000..b39edc5
--- /dev/null
+++ b/TankMan/command.txt
@@ -0,0 +1 @@
+python -m mlgame -f 10 -i ml/ml_play_manual.py -i ml/ml_play_manual.py -i ml/ml_play_manual.py -i ml/ml_play_manual.py -i ml/ml_play_manual.py -i ml/ml_play_manual.py . --green_team_num 3 --blue_team_num 3 --is_manual 1 --frame_limit 1000
\ No newline at end of file
diff --git a/TankMan/config.py b/TankMan/config.py
new file mode 100644
index 0000000..1b7f709
--- /dev/null
+++ b/TankMan/config.py
@@ -0,0 +1,10 @@
+import sys
+from os import path
+
+sys.path.append(path.dirname(__file__))
+from src.Game import Game
+
+GAME_SETUP = {
+ "game": Game,
+}
+
diff --git a/TankMan/contest.py b/TankMan/contest.py
new file mode 100644
index 0000000..c1e3425
--- /dev/null
+++ b/TankMan/contest.py
@@ -0,0 +1,137 @@
+import pygame
+from mlgame.game.generic import quit_or_esc
+from src.Game import Game
+from tqdm import tqdm
+import argparse
+import importlib
+
+
+
+class Contest():
+ """
+ This class manages a 3 vs 3 game contest.
+
+ Parameters
+ ----------
+ player : list
+ The list of players.
+ total_game : int
+ The total number of games.
+ frame : int
+ The frame of the game.
+ sound : str
+ The sound of the game.
+ is_manual : bool
+ The manual mode of the game.
+
+ Returns
+ -------
+ dict
+ The result of the game.
+ """
+ def __init__(self, player: list, total_game: int, frame: int,
+ sound: str, is_manual: bool):
+ # initialize player
+ self.player1 = player[0]
+ self.player2 = player[1]
+ self.player3 = player[2]
+ self.player4 = player[3]
+ self.player5 = player[4]
+ self.player6 = player[5]
+
+ # initialize game
+ self.total_game = total_game
+ self.frame = frame
+ self.sound = sound
+ self.is_manual = is_manual
+
+ self.user_num = 6
+ self.green_team_num = 3
+ self.blue_team_num = 3
+
+ # initialize game times
+ self.game_times = 0
+ self.green_team_win = 0
+ self.blue_team_win = 0
+
+ def run(self):
+ with tqdm(total=self.total_game, unit="round") as pbar:
+ pbar.set_description("Playing")
+ print()
+ for _ in range(self.total_game):
+ # initialize game every round
+ game = Game(self.user_num, self.green_team_num,
+ self.blue_team_num, self.is_manual, self.frame, self.sound)
+ scene_init_info_dict = game.get_scene_init_data()
+ frame_count = 0
+
+ # update game
+ while game.is_running() and not quit_or_esc():
+ game.update({
+ "1P": self.player1.update(game.get_data_from_game_to_player()['1P'], []),
+ "2P": self.player2.update(game.get_data_from_game_to_player()['2P'], []),
+ "3P": self.player3.update(game.get_data_from_game_to_player()['3P'], []),
+ "4P": self.player4.update(game.get_data_from_game_to_player()['4P'], []),
+ "5P": self.player5.update(game.get_data_from_game_to_player()['5P'], []),
+ "6P": self.player6.update(game.get_data_from_game_to_player()['6P'], [])
+ })
+ frame_count += 1
+
+ # reset player
+ self.player1.reset()
+ self.player2.reset()
+ self.player3.reset()
+ self.player4.reset()
+ self.player5.reset()
+ self.player6.reset()
+ self.game_times += 1
+
+ # get game result
+ game_result = game.get_game_result()
+ if game_result['attachment'][0]["status"] == "GREEN_TEAM_WIN":
+ self.green_team_win += 1
+ else:
+ self.blue_team_win += 1
+
+ # update progress bar
+ pbar.postfix = f"green_team win: {self.green_team_win} | blue_team win: {self.blue_team_win}"
+ pbar.update(1)
+ # print()
+ if self.green_team_win > self.total_game/2 or self.blue_team_win > self.total_game/2:
+ break
+
+
+ print(f"Game times: {self.game_times} | green_team win: {self.green_team_win} | blue_team win: {self.blue_team_win}")
+ pygame.quit()
+ return {"green_team_win":self.green_team_win, "blue_team_win":self.blue_team_win}
+
+def import_player(group_number, player_number):
+ module_name = f"ml.Group_{group_number}.ml_play_{player_number}"
+ module = importlib.import_module(module_name)
+ return getattr(module, f"MLPlay")
+
+if __name__ == '__main__':
+ # Set configuration
+ sound = "off"
+ total_game = 9
+ frame = 2500
+ is_manual = False
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--green_team", type=str, required=True, help="The folder of the green team")
+ parser.add_argument("--blue_team", type=str, required=True, help="The folder of the blue team")
+ args = parser.parse_args()
+ print(args)
+ player1 = import_player(1, 1)
+
+
+ players = [ import_player(args.green_team, 1)('1P', {'sound': sound}),
+ import_player(args.green_team, 2)('2P', {'sound': sound}),
+ import_player(args.green_team, 3)('3P', {'sound': sound}),
+ import_player(args.blue_team, 1)('4P', {'sound': sound}),
+ import_player(args.blue_team, 2)('5P', {'sound': sound}),
+ import_player(args.blue_team, 3)('6P', {'sound': sound})]
+
+ contest = Contest(players, total_game , frame, sound, is_manual)
+ contest.run()
+
\ No newline at end of file
diff --git a/TankMan/doc/ClassDiagram.mdj b/TankMan/doc/ClassDiagram.mdj
new file mode 100644
index 0000000..1470809
--- /dev/null
+++ b/TankMan/doc/ClassDiagram.mdj
@@ -0,0 +1,9792 @@
+{
+ "_type": "Project",
+ "_id": "AAAAAAFF+h6SjaM2Hec=",
+ "name": "Untitled",
+ "ownedElements": [
+ {
+ "_type": "UMLModel",
+ "_id": "AAAAAAFF+qBWK6M3Z8Y=",
+ "_parent": {
+ "$ref": "AAAAAAFF+h6SjaM2Hec="
+ },
+ "name": "Model",
+ "ownedElements": [
+ {
+ "_type": "UMLClassDiagram",
+ "_id": "AAAAAAFF+qBtyKM79qY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Main",
+ "defaultDiagram": true,
+ "ownedViews": [
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAXjxqhKVg3/0=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAXjxqhKVhfuo=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAXjxqhKViQ6o=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVhfuo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 352,
+ "top": -240,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAXjxqhKVjFeI=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVhfuo="
+ },
+ "font": "Arial;13;1",
+ "left": 365,
+ "top": 103,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "GameMode"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAXjxqhKVkrw8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVhfuo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 352,
+ "top": -240,
+ "width": 80.5009765625,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAXjxqhKVlyhY=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVhfuo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 352,
+ "top": -240,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 360,
+ "top": 96,
+ "width": 404.18310546875,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAXjxqhKViQ6o="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAXjxqhKVjFeI="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAXjxqhKVkrw8="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAXjxqhKVlyhY="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAXjxqhKVmFgA=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk0r8q6XP1/0=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk0r8lqXMu2M="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 126,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+all_sprites: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAXj2LdaWRPC8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXj2LaKWObGk="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 357,
+ "top": 229,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+bullets: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk3nQ16Yp4OE=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk3nQ0KYmHrg="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 357,
+ "top": 244,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+walls: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk3yvcaZFFZ8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk3yvZqZCZa4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 357,
+ "top": 244,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+mobs: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk28mHqYSbJo=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk28mGKYPh40="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 357,
+ "top": 244,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+player_1P: Sprite",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk2WvzqYLrL4=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk2WvwaYITAc="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 141,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+is_paused: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk3fk/qYZjoY=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk3fk+KYW84M="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 357,
+ "top": 304,
+ "width": 386.953125,
+ "height": 13,
+ "text": "+is_playing: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk3hGl6YhMEk=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk3hGkKYe19M="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 156,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+is_sound: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk2Un9aYEJdY=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk2Un6KYBvzo="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 171,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+used_frame: Int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmBTs97difk8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmBTs57dfR+A="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 186,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+frame_limit: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk3qQWaYwdgE=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk3qQUqYtB2U="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 201,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+window: pygame.Surface",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl1t4HUO/dCI=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl1t4D0O8V40="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 216,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+map: TiledMap",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmAyRz1DW6g8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmAyRvVDTY88="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 231,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+state: Str",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmAzqtFQs0MI=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmAzqn1QpDMk="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 246,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+status: str",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 360,
+ "top": 121,
+ "width": 404.18310546875,
+ "height": 143
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAXjxqhKVnmdQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk15L26XXJXg=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk15Ly6XULl4="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 269,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "-__init__(map_path: Str, time_limit: Int, sound: Bool): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk171BaXfoSI=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk1709KXc5+4="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 284,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+get_result(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk18966XnW1M=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk1894KXkSJ8="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 299,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+updata(command: Dict): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk2Bn9qXwSdI=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk2Bn7qXt7o8="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 314,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+check_events(): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk2DZO6X3pOY=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk2DZMqX0aQQ="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 329,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+check_collisions(): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAmBa4nrpzsm0=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmBa4jbpw4xo="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 344,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+reset(): tuple",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAtcd4HZ059lE=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAtcd3+p02Ti8="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 359,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+draw_sprite_data()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAtcf9VqFpfBw=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAtcf9QKFmM8o="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 374,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+create_init_image_data()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAtcgsAaVdmuU=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAtcgr7KVa4xs="
+ },
+ "font": "Arial;13;0",
+ "left": 365,
+ "top": 389,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+draw_text_data()",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 360,
+ "top": 264,
+ "width": 404.18310546875,
+ "height": 143
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAXjxqhaVoJSQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 240,
+ "top": 423,
+ "width": 467.7685546875,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAXjxqhaVp7OU=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 576.099609375,
+ "top": 112,
+ "width": 130.6689453125,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 360,
+ "top": 96,
+ "width": 404.18310546875,
+ "height": 311,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAXjxqhKVhfuo="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAXjxqhKVmFgA="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAXjxqhKVnmdQ="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAXjxqhaVoJSQ="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAXjxqhaVp7OU="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAk4Bm7aZpz/0=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAk4Bm7qZqCE0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4Bm7qZr+e0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7qZqCE0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -119.75390625,
+ "top": 704,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4Bm7qZs/Io=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7qZqCE0="
+ },
+ "font": "Arial;13;1",
+ "left": 581,
+ "top": 423,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "BattleMode"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4Bm7qZtSmM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7qZqCE0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -119.75390625,
+ "top": 704,
+ "width": 80.5009765625,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4Bm7qZuKO0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7qZqCE0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -119.75390625,
+ "top": 704,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 576,
+ "top": 416,
+ "width": 190.29248046875,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAk4Bm7qZr+e0="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAk4Bm7qZs/Io="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAk4Bm7qZtSmM="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAk4Bm7qZuKO0="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAk4Bm76ZveFI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk4VFMKjuSLk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4VFJajrfOU="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 446,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+player_2P: sprite",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl1ODOCYWYvI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl1ODLSYTr98="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 461,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+is_invincible: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl1PKdipOYaE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl1PKaSpLQKM="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 476,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+is_through_wall: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl1aZozObuwc=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl1aZlTOYSFU="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 491,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+is_debug: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmCsj9cv2NVk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmCsj48vzQzY="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 506,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+players: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmCtP7szDy5U=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmCtP1szA3Lk="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 521,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+walls: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmCt9Vs20WxQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmCt9Oc2xkSk="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 536,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+bullets: Group",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmCu0Vc6lhlE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmCu0Q86iDIA="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 551,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+player_1P: sprite",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 576,
+ "top": 441,
+ "width": 190.29248046875,
+ "height": 128
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAk4Bm76Zw1Xw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAq751YuNJ2eg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76Zw1Xw="
+ },
+ "model": {
+ "$ref": "AAAAAAGAq751PONGLwk="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 574,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+create_bullte(shoot_info: Dict)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAtepAK7C2mW4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76Zw1Xw="
+ },
+ "model": {
+ "$ref": "AAAAAAGAtepAErCzCDo="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 589,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+draw_sprite_data(): list",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAtep9Z7Fz12E=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76Zw1Xw="
+ },
+ "model": {
+ "$ref": "AAAAAAGAtep9JrFwneU="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 604,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+create_init_image_data(): list",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAteq33rJOgvA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm76Zw1Xw="
+ },
+ "model": {
+ "$ref": "AAAAAAGAteq3xbJLFFI="
+ },
+ "font": "Arial;13;0",
+ "left": 581,
+ "top": 619,
+ "width": 180.29248046875,
+ "height": 13,
+ "text": "+draw_text_data(): list",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 576,
+ "top": 569,
+ "width": 190.29248046875,
+ "height": 68
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAk4Bm76Zxl3k=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -59.876953125,
+ "top": 352,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAk4Bm76ZyuiE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -59.876953125,
+ "top": 352,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 576,
+ "top": 416,
+ "width": 190.29248046875,
+ "height": 221,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAk4Bm7qZqCE0="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAk4Bm76ZveFI="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAk4Bm76Zw1Xw="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAk4Bm76Zxl3k="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAk4Bm76ZyuiE="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAk4i0r6t962o=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAk4i0r6t+wI0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4i0r6t/y6M=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t+wI0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -640,
+ "top": 48,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4i0r6uA39Y=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t+wI0="
+ },
+ "font": "Arial;13;1",
+ "left": 29,
+ "top": 31,
+ "width": 318.5,
+ "height": 13,
+ "text": "MyGame"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4i0r6uB5nI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t+wI0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -640,
+ "top": 48,
+ "width": 80.5009765625,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk4i0r6uCuto=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t+wI0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -640,
+ "top": 48,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 24,
+ "top": 24,
+ "width": 328.5,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAk4i0r6t/y6M="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAk4i0r6uA39Y="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAk4i0r6uB5nI="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAk4i0r6uCuto="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAk4i0r6uDN3g=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlvY4CzFRUfg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uDN3g="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlvY38TFLyd8="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 54,
+ "width": 318.5,
+ "height": 13,
+ "text": "+scene: Scene",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk4kgA6ybYog=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uDN3g="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4kf+KyVMfg="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 69,
+ "width": 318.5,
+ "height": 13,
+ "text": "+attachements: List",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk4l9CKzDTl0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uDN3g="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4l896y9t7w="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 84,
+ "width": 318.5,
+ "height": 13,
+ "text": "+game_mode: Q",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk4qz5qzxHNU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uDN3g="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4qz3azr1dY="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 99,
+ "width": 318.5,
+ "height": 13,
+ "text": "+sound: Str",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk4uV8q0fse8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uDN3g="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4uV6a0Zir8="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 114,
+ "width": 318.5,
+ "height": 13,
+ "text": "+map_path: Str",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 24,
+ "top": 49,
+ "width": 328.5,
+ "height": 83
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAk4i0r6uEj2E=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlvcFUDJ81VM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uEj2E="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlvcFRzJ2WKg="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 137,
+ "width": 318.5,
+ "height": 13,
+ "text": "-__init__(map_no: int, sound: str)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5zhEdNShKA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uEj2E="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5zhAtNMoaw="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 152,
+ "width": 318.5,
+ "height": 13,
+ "text": "+get_scene_info(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk52n7tP4qoQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uEj2E="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk52n3dPyZ8c="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 167,
+ "width": 318.5,
+ "height": 13,
+ "text": "+is_running(): Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk54ny9RB5j4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uEj2E="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk54nv9Q7Idw="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 182,
+ "width": 318.5,
+ "height": 13,
+ "text": "+set_game_mode(game_path: Str, is_sound: Bool): Q",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk6KdYdwc+vo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6uEj2E="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6KdVtwW3yQ="
+ },
+ "font": "Arial;13;0",
+ "left": 29,
+ "top": 197,
+ "width": 318.5,
+ "height": 13,
+ "text": "+rank(): List",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 24,
+ "top": 132,
+ "width": 328.5,
+ "height": 83
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAk4i0r6uFBB8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -344,
+ "top": 376,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAk4i0r6uGRCI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -344,
+ "top": 376,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 24,
+ "top": 24,
+ "width": 328.5,
+ "height": 191,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAk4i0r6t+wI0="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAk4i0r6uDN3g="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAk4i0r6uEj2E="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAk4i0r6uFBB8="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAk4i0r6uGRCI="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGAk5CJqcAtdYk=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5CJqcAr7io="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAk5CJqcAubIk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5CJqcAtdYk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5CJqcAr7io="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 786,
+ "top": 404,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAk5CJqcAtdYk="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAk5CJqcAvgl8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5CJqcAtdYk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5CJqcAr7io="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 777,
+ "top": 392,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAk5CJqcAtdYk="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAk5CJqcAwXOw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5CJqcAtdYk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5CJqcAr7io="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 805,
+ "top": 427,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAk5CJqcAtdYk="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "lineStyle": 1,
+ "points": "766:447;827:397",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGAk5CJqcAubIk="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAk5CJqcAvgl8="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAk5CJqcAwXOw="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAk5DXD8IZ4r8=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAk5DXD8IaSU8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IZ4r8="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk5DXD8Ib+pI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IaSU8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -160,
+ "top": 488,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk5DXD8IcbA8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IaSU8="
+ },
+ "font": "Arial;13;1",
+ "left": 21,
+ "top": 255,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "PaiaGame"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk5DXEMIdke8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IaSU8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -160,
+ "top": 488,
+ "width": 80.5009765625,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk5DXEMIehaA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IaSU8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -160,
+ "top": 488,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 16,
+ "top": 248,
+ "width": 211.59521484375,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAk5DXD8Ib+pI="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAk5DXD8IcbA8="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAk5DXEMIdke8="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAk5DXEMIehaA="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAk5DXEMIfFPU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IZ4r8="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk5G7IsROG2U=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIfFPU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5G7FsRFRTg="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 278,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+scene: Scene",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk5H9TcSXHAo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIfFPU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5H9QsSOiRk="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 293,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+frame_count: Int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk5I3I8TsSMs=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIfFPU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5I3GsTjZv4="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 308,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+game_result_state: Str",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 16,
+ "top": 273,
+ "width": 211.59521484375,
+ "height": 53
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAk5DXEMIgZ94=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IZ4r8="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5W54MWzVno=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5W51cWqrZ4="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 331,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "-__init__(): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5XwtcYJ9VM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5XwpMYA/gU="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 346,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+update(commands: Dict): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5a2Iscr8jY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5a2GMciKVk="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 361,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+game_to_player_data(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5dBTMeIj9Q=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5dBQsd/Tmw="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 376,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+reset(): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5domMflXek=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5dojsfcCqM="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 391,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+get_scene_init_data(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5fEPMhCQh4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5fEMsg5d4g="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 406,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+get_scene_progress_data(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5gs98ifyUA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5gs7ciWGr0="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 421,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+get_game_result(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5iCs8j8mrU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5iCl8jzZ5c="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 436,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+get_keyboard_command(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk5jVyMlZq9w=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5jVvclQBvY="
+ },
+ "font": "Arial;13;0",
+ "left": 21,
+ "top": 451,
+ "width": 201.59521484375,
+ "height": 13,
+ "text": "+ai_clients(): List",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 16,
+ "top": 326,
+ "width": 211.59521484375,
+ "height": 143
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAk5DXEMIhvH8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IZ4r8="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -88,
+ "top": 12,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAk5DXEMIiohQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IZ4r8="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -88,
+ "top": 12,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 16,
+ "top": 248,
+ "width": 211.59521484375,
+ "height": 236,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAk5DXD8IaSU8="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAk5DXEMIfFPU="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAk5DXEMIgZ94="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAk5DXEMIhvH8="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAk5DXEMIiohQ="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGAk5DXmsJgVjE=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXmsJevOw="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAk5DXm8JhxFo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXmsJgVjE="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXmsJevOw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 171,
+ "top": 228,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAk5DXmsJgVjE="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAk5DXm8Ji+y8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXmsJgVjE="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXmsJevOw="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 186,
+ "top": 232,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAk5DXmsJgVjE="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAk5DXm8JjUqk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXmsJgVjE="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk5DXmsJevOw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 142,
+ "top": 221,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAk5DXmsJgVjE="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAk5DXD8IZ4r8="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "lineStyle": 1,
+ "points": "161:215;153:247",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGAk5DXm8JhxFo="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAk5DXm8Ji+y8="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAk5DXm8JjUqk="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAk6h2wehM6EA=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAk6h2wuhNu7g=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehM6EA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk6h2wuhOXKU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhNu7g="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -768,
+ "top": 232,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk6h2wuhP+6c=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhNu7g="
+ },
+ "font": "Arial;13;1",
+ "left": 261,
+ "top": 423,
+ "width": 277.1005859375,
+ "height": 13,
+ "text": "TiledMap"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk6h2wuhQbJw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhNu7g="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -768,
+ "top": 232,
+ "width": 80.5009765625,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAk6h2wuhRme8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhNu7g="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -768,
+ "top": 232,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 256,
+ "top": 416,
+ "width": 287.1005859375,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAk6h2wuhOXKU="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAk6h2wuhP+6c="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAk6h2wuhQbJw="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAk6h2wuhRme8="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAk6h2wuhSoMU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehM6EA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk6urkPGUWjE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhSoMU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6urfvGImRk="
+ },
+ "font": "Arial;13;0",
+ "left": 261,
+ "top": 446,
+ "width": 277.1005859375,
+ "height": 13,
+ "text": "+width: Int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk6vszfH+NxY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhSoMU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6vsvvHyyiw="
+ },
+ "font": "Arial;13;0",
+ "left": 261,
+ "top": 461,
+ "width": 277.1005859375,
+ "height": 13,
+ "text": "+height: Int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAk6wt4vMBLAI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhSoMU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6wt1vL1d7s="
+ },
+ "font": "Arial;13;0",
+ "left": 261,
+ "top": 476,
+ "width": 277.1005859375,
+ "height": 13,
+ "text": "+tmx_data: pytmx.pytmx.TieldMap",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 256,
+ "top": 441,
+ "width": 287.1005859375,
+ "height": 53
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAk6h2wuhTxI0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehM6EA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk6ksCe4OGQg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhTxI0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6kr/O4CXBo="
+ },
+ "font": "Arial;13;0",
+ "left": 261,
+ "top": 499,
+ "width": 277.1005859375,
+ "height": 13,
+ "text": "+creat_img_init_data(img_no_list: List): List",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk6l+2u54DCQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhTxI0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6l+y+5sE9E="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 285,
+ "top": 346,
+ "width": 313.9677734375,
+ "height": 13,
+ "text": "+creat_walls_init_data(walls_img_no_list: List): List",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAk6phAPChJiA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhTxI0="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6pg8fCVFEY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 285,
+ "top": 346,
+ "width": 313.9677734375,
+ "height": 13,
+ "text": "+creat_mobs_init_data(mobs_img_no_list: List): List",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArQUJmV61oCY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wuhTxI0="
+ },
+ "model": {
+ "$ref": "AAAAAAGArQUJgl6yVkg="
+ },
+ "font": "Arial;13;0",
+ "left": 261,
+ "top": 514,
+ "width": 277.1005859375,
+ "height": 13,
+ "text": "-__init__(map_no: int, time_limit: int, sound: str)",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 256,
+ "top": 494,
+ "width": 287.1005859375,
+ "height": 38
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAk6h2wuhU99M=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehM6EA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -288,
+ "top": 172,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAk6h2wuhVloA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehM6EA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -288,
+ "top": 172,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 256,
+ "top": 416,
+ "width": 287.1005859375,
+ "height": 116,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAk6h2wuhNu7g="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAk6h2wuhSoMU="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAk6h2wuhTxI0="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAk6h2wuhU99M="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAk6h2wuhVloA="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAlv9Mx0pY6qQ=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAlv9Mx0pZhO0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pY6qQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlv9Mx0paOYA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pZhO0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 448,
+ "top": -44,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlv9Mx0pbtOw=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pZhO0="
+ },
+ "font": "Arial;13;1",
+ "left": 997,
+ "top": 343,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "Player"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlv9Mx0pcbck=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pZhO0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 448,
+ "top": -44,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlv9Mx0pdb84=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pZhO0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 448,
+ "top": -44,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 992,
+ "top": 336,
+ "width": 180.5234375,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAlv9Mx0paOYA="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAlv9Mx0pbtOw="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAlv9Mx0pcbck="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAlv9Mx0pdb84="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAlv9MyEpe3uI=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pY6qQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwJoeV/2edA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwJoZ1/tBu0="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 366,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+img_id: str",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwGUd1376rk=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwGUa13yup4="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 381,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+is_shoot: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwMnQWKNXEI=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwMnN2KEupU="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 396,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+is_backward: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwOVBmMwtsk=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwOU8WMn6Wc="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 411,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+is_forward: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwPf8WeTv4c=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwPf6GeKsQk="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 426,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+last_shoot_fram: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwR9n2g22ak=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwR9kGgtS2k="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 441,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+shoot_delay: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwTts2xqMfk=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwTtomxhH4o="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 456,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+shield: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwdG4HGm9+g=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwdG1XGdV3I="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 471,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+rot: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwdqMHJJf0w=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwdqIHJA7mw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 1101,
+ "top": 828,
+ "width": 211.35791015625,
+ "height": 13,
+ "text": "+rot_speed: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwW7wG2q6Ss=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwW7rW2h/mA="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 486,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+move: List",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwibkXTVp6A=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwibfnTMDYs="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 501,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+surface: Surface",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlwk/RXa+dAQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwk/Nna1N4Q="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 1077,
+ "top": 886,
+ "width": 136.92529296875,
+ "height": 13,
+ "text": "+Attribute1",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArUoSfJBjhiE=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "model": {
+ "$ref": "AAAAAAGArUoSY5Bgse4="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 516,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+angle: Float",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 992,
+ "top": 361,
+ "width": 180.5234375,
+ "height": 173
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAlv9MyEpf2Ec=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pY6qQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwn1BIAzLCo=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwn094AqVB0="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 539,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "-__init__(_no: int, x: int, y: int)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwpCMYEBCeo=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwpCJoD4hM4="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 554,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+update(command: Dic)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwqMRoGayT8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwqMMIGRwsA="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 569,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+act(commands: Dic)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlws/54MYAqk=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlws/04MPOTg="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 584,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+forward()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwtheoO7yhU=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwthaYOyjnQ="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 599,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+backward()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwt+3oReS/0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwt+z4RV5c4="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 614,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+turn_left()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwuZGYUB5Vg=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwuZCYT4X14="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 629,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+turn_right()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwwF8obqJr0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwwF3obhmr0="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 644,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+collide_with_walls()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlwwt6IeNlQU=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlwwt14eEiHY="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 659,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+collide_with_bullets()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl2lm5lFj/Z8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl2lm1lFgub0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 1213,
+ "top": 722,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+create_shoot_info(): dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl2qJLlMOaLA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl2qJGlMLItA="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 674,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+rotate()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAq8OBx+ZYiD8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGAq8OBs+ZVm28="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 689,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+create_shoot_info(): dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArUpRQpUaoHI=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "model": {
+ "$ref": "AAAAAAGArUpRK5UXbhM="
+ },
+ "font": "Arial;13;0",
+ "left": 997,
+ "top": 704,
+ "width": 170.5234375,
+ "height": 13,
+ "text": "+get_image_data(): dict",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 992,
+ "top": 534,
+ "width": 180.5234375,
+ "height": 188
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAlv9MyEpgjz4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pY6qQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 224,
+ "top": -22,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAlv9MyEphQ6U=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9Mx0pY6qQ="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 224,
+ "top": -22,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 992,
+ "top": 336,
+ "width": 180.5234375,
+ "height": 386,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAlv9Mx0pZhO0="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAlv9MyEpe3uI="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAlv9MyEpf2Ec="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAlv9MyEpgjz4="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAlv9MyEphQ6U="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAlw1a9IrL4dk=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAlw1a9IrMeBs=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrL4dk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlw1a9IrN1Bs=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrMeBs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -1824,
+ "top": -170,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlw1a9IrOVxU=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrMeBs="
+ },
+ "font": "Arial;13;1",
+ "left": 5,
+ "top": 703,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "Obstacle"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlw1a9IrP9gw=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrMeBs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -1824,
+ "top": -170,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAlw1a9IrQtDI=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrMeBs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -1824,
+ "top": -170,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "top": 696,
+ "width": 298.6787109375,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAlw1a9IrN1Bs="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAlw1a9IrOVxU="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAlw1a9IrP9gw="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAlw1a9IrQtDI="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAlw1a9IrRNwU=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrL4dk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlw22ZI7Zsgc=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrRNwU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw22UY7K4Ig="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 726,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "+rect_ Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlw3Xt4+F++s=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrRNwU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw3Xno92lR8="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 741,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "+hit_rect: Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlw4hFZBPDx4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrRNwU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw4hA5BA2UQ="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -387,
+ "top": 1060,
+ "width": 210.35791015625,
+ "height": 13,
+ "text": "+pos: Vector2",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAlw75AJRRjBE=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrRNwU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw748pRCH1Q="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 756,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "+lives: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBgSPQYySr4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrRNwU="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBgSKQYpZa8="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 771,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "#_no: int",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "top": 721,
+ "width": 298.6787109375,
+ "height": 68
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAlw1a9IrSjAU=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrL4dk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlw5+w5IYpT4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrSjAU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw5+s5IJsbI="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 794,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "+updata()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAlw67aZLE+h8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrSjAU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw67WJK1kyc="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 809,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "+get_position(): Tuple",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArBePAQFs+5I=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrSjAU="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBeO6QFjVkw="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 824,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "+collide_with_bullets()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArP0e7UMxD/Q=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrSjAU="
+ },
+ "model": {
+ "$ref": "AAAAAAGArP0eyUMoi6I="
+ },
+ "font": "Arial;13;0",
+ "left": 5,
+ "top": 839,
+ "width": 288.6787109375,
+ "height": 13,
+ "text": "-__init__(no: int, x: int, y: int, width: int, height: int)",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "top": 789,
+ "width": 298.6787109375,
+ "height": 68
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAlw1a9IrT+tA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrL4dk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -1096,
+ "top": -93,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAlw1a9IrUxfs=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrL4dk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -1096,
+ "top": -93,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "top": 696,
+ "width": 298.6787109375,
+ "height": 161,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAlw1a9IrMeBs="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAlw1a9IrRNwU="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAlw1a9IrSjAU="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAlw1a9IrT+tA="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAlw1a9IrUxfs="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAl12L1ka5A8Q=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAl12L1ka6NJ4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl12L1ka7P2I=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka6NJ4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 272,
+ "top": 16,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl12L1ka8ckg=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka6NJ4="
+ },
+ "font": "Arial;13;1",
+ "left": 1029,
+ "top": 15,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "Player"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl12L1ka9ebA=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka6NJ4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 272,
+ "top": 16,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl12L1ka+OH4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka6NJ4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 272,
+ "top": 16,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 1024,
+ "top": 8,
+ "width": 149.08984375,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAl12L1ka7P2I="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAl12L1ka8ckg="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAl12L1ka9ebA="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAl12L1ka+OH4="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAl12L1ka/mhk=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl398qJKqDvw=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl3x7tYZrEgc="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 38,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+hit_rect: pygame.Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl3+CMpMcxwo=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl31Nc4jZfC8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 1269,
+ "top": 53,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+img_path: str",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl3+Hw5OOYsU=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl34Jtou/xeM="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 53,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+player_info: Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl3/jAJYEckM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl3/i6ZX1W/I="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 68,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+result_info: Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4FjfpjeFsA=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4FjaZjP9Js="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 901,
+ "top": 90,
+ "width": 134.037109375,
+ "height": 13,
+ "text": "+pos: Vector2",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4L7UZvFCFQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4L7QJu261g="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 83,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+rect: pygame.Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4Vhd53T+NA=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4VhZ53EuA8="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 98,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+score: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4WdzqAp1Cw=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4Wdu6AacQY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 917,
+ "top": 120,
+ "width": 211.35791015625,
+ "height": 13,
+ "text": "+speed: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4hB2aRF/Eg=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4hBwaQ2YAk="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 113,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+used_frame: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4gFRqI3oNQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4gFNKIok+Q="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 128,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+is_alive: Bool",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4h486anD/w=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4h44qaYN9Y="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 143,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+vel: Vector2",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAmGGmPtct39A=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAmGGmHNceHdk="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 158,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "#_no: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArT+nSIcAV9s=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "model": {
+ "$ref": "AAAAAAGArT+nLob0qfY="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 173,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+image_data: dict",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 1024,
+ "top": 33,
+ "width": 149.08984375,
+ "height": 158
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAl12L1kbADQY=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4kQqKoG548=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4kQlqn3uu8="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 196,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+update(command: Dic)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4qWQbK7VRI=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4qWLbKs5LA="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 211,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+act(commands: Dic)",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4nZ1qwiy3Q=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4nZw6wTnwM="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 226,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+get_info(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4onGq5Wz6U=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4onB65HF+4="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 241,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+get_result(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4wLLbX58po=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4wLGLXqvNE="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 256,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+get_position(): Tuple",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4x9s7hDReM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4x9obg0ows="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 271,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+player_init_data(): Dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArT0E/nw2lyg=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArT0E5nwq/LE="
+ },
+ "font": "Arial;13;0",
+ "left": 1029,
+ "top": 286,
+ "width": 139.08984375,
+ "height": 13,
+ "text": "+get_image_data()",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 1024,
+ "top": 191,
+ "width": 149.08984375,
+ "height": 113
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAl12L1kbB64I=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 136,
+ "top": 8,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAl12L1kbCaUI=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 136,
+ "top": 8,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 1024,
+ "top": 8,
+ "width": 149.08984375,
+ "height": 296,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAl12L1ka6NJ4="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAl12L1ka/mhk="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAl12L1kbADQY="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAl12L1kbB64I="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAl12L1kbCaUI="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGAl12MsEceznU=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12MsEcchKs="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl12MsEcfArU=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12MsEceznU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12MsEcchKs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 1075,
+ "top": 312,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl12MsEceznU="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl12MsEcgDbM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12MsEceznU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12MsEcchKs="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 1060,
+ "top": 311,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl12MsEceznU="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl12MsEchIRI=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12MsEceznU="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl12MsEcchKs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 1104,
+ "top": 313,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl12MsEceznU="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAlv9Mx0pY6qQ="
+ },
+ "lineStyle": 1,
+ "points": "1090:335;1091:304",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGAl12MsEcfArU="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAl12MsEcgDbM="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAl12MsEchIRI="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAl441S8CcU7A=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAl441S8Cd7Tw=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl441S8CeK/o=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8Cd7Tw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -304,
+ "top": 56,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl441S8CfcB4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8Cd7Tw="
+ },
+ "font": "Arial;13;1",
+ "left": 245,
+ "top": 567,
+ "width": 246.04150390625,
+ "height": 13,
+ "text": "Prop"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl441S8CgycI=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8Cd7Tw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -304,
+ "top": 56,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAl441S8ChyJE=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8Cd7Tw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -304,
+ "top": 56,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 240,
+ "top": 560,
+ "width": 256.04150390625,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAl441S8CeK/o="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAl441S8CfcB4="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAl441S8CgycI="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAl441S8ChyJE="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAl441S8Ci2Z4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl48EeMUoYnM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8Ci2Z4="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl48EYsUZjkQ="
+ },
+ "font": "Arial;13;0",
+ "left": 245,
+ "top": 590,
+ "width": 246.04150390625,
+ "height": 13,
+ "text": "+rect: pygame.Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGAl4+pScmWinQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8Ci2Z4="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4+pJsmHF4I="
+ },
+ "font": "Arial;13;0",
+ "left": 245,
+ "top": 605,
+ "width": 246.04150390625,
+ "height": 13,
+ "text": "+hit_rect: Rect",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 240,
+ "top": 585,
+ "width": 256.04150390625,
+ "height": 38
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAl441S8CjgDM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl47ItMNf4UA=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CjgDM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl47IncNQv5A="
+ },
+ "font": "Arial;13;0",
+ "left": 245,
+ "top": 628,
+ "width": 246.04150390625,
+ "height": 13,
+ "text": "+update()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAl4/eIMubbx8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CjgDM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl4/eCsuMa9A="
+ },
+ "font": "Arial;13;0",
+ "left": 245,
+ "top": 643,
+ "width": 246.04150390625,
+ "height": 13,
+ "text": "+get_position(): Tuple",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArPxSekCM6hs=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CjgDM="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPxSYkB9Khg="
+ },
+ "font": "Arial;13;0",
+ "left": 245,
+ "top": 658,
+ "width": 246.04150390625,
+ "height": 13,
+ "text": "-__init__(x: int, y: int, width: int, height: int)",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 240,
+ "top": 623,
+ "width": 256.04150390625,
+ "height": 53
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAl441TMCkB7U=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -152,
+ "top": 28,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAl441TMClmg4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -152,
+ "top": 28,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 240,
+ "top": 560,
+ "width": 256.04150390625,
+ "height": 116,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAl441S8Cd7Tw="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAl441S8Ci2Z4="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAl441S8CjgDM="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAl441TMCkB7U="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAl441TMClmg4="
+ }
+ },
+ {
+ "_type": "UMLAssociationView",
+ "_id": "AAAAAAGAl57vqRbqIPA=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbm3Ro="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbrBRQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbm3Ro="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 455,
+ "top": 414,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbs148=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbm3Ro="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 467,
+ "top": 423,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbt3G8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbm3Ro="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 432,
+ "top": 395,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbuUfI=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbn1Y8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 443,
+ "top": 430,
+ "height": 13,
+ "alpha": 0.5235987755982988,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "edgePosition": 2
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbvDvc=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbn1Y8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 452,
+ "top": 440,
+ "height": 13,
+ "alpha": 0.7853981633974483,
+ "distance": 40,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "edgePosition": 2
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbwGe8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbn1Y8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 424,
+ "top": 410,
+ "height": 13,
+ "alpha": -0.5235987755982988,
+ "distance": 25,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "edgePosition": 2
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbxOng=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbo+Ck="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 468,
+ "top": 397,
+ "height": 13,
+ "alpha": -0.5235987755982988,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ }
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbyFts=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbo+Ck="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 480,
+ "top": 403,
+ "height": 13,
+ "alpha": -0.7853981633974483,
+ "distance": 40,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ }
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAl57vqhbzhRM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbo+Ck="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 443,
+ "top": 384,
+ "height": 13,
+ "alpha": 0.5235987755982988,
+ "distance": 25,
+ "hostEdge": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ }
+ },
+ {
+ "_type": "UMLQualifierCompartmentView",
+ "_id": "AAAAAAGAl57vqhb0LIo=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbn1Y8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 480,
+ "top": 120,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLQualifierCompartmentView",
+ "_id": "AAAAAAGAl57vqhb10SU=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqRbqIPA="
+ },
+ "model": {
+ "$ref": "AAAAAAGAl57vqBbo+Ck="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 480,
+ "top": 120,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAk6h2wehM6EA="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "lineStyle": 1,
+ "points": "447:407;441:415",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGAl57vqhbrBRQ="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAl57vqhbs148="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAl57vqhbt3G8="
+ },
+ "showEndOrder": "hide",
+ "tailRoleNameLabel": {
+ "$ref": "AAAAAAGAl57vqhbuUfI="
+ },
+ "tailPropertyLabel": {
+ "$ref": "AAAAAAGAl57vqhbvDvc="
+ },
+ "tailMultiplicityLabel": {
+ "$ref": "AAAAAAGAl57vqhbwGe8="
+ },
+ "headRoleNameLabel": {
+ "$ref": "AAAAAAGAl57vqhbxOng="
+ },
+ "headPropertyLabel": {
+ "$ref": "AAAAAAGAl57vqhbyFts="
+ },
+ "headMultiplicityLabel": {
+ "$ref": "AAAAAAGAl57vqhbzhRM="
+ },
+ "tailQualifiersCompartment": {
+ "$ref": "AAAAAAGAl57vqhb0LIo="
+ },
+ "headQualifiersCompartment": {
+ "$ref": "AAAAAAGAl57vqhb10SU="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGArBYryu8whqg=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBYrye8uD2Q="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArBYryu8xNlU=",
+ "_parent": {
+ "$ref": "AAAAAAGArBYryu8whqg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBYrye8uD2Q="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 264,
+ "top": 666,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGArBYryu8whqg="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArBYryu8yVRc=",
+ "_parent": {
+ "$ref": "AAAAAAGArBYryu8whqg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBYrye8uD2Q="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 255,
+ "top": 654,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGArBYryu8whqg="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArBYryu8zJHk=",
+ "_parent": {
+ "$ref": "AAAAAAGArBYryu8whqg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBYrye8uD2Q="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 281,
+ "top": 691,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGArBYryu8whqg="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAlw1a9IrL4dk="
+ },
+ "lineStyle": 1,
+ "points": "260:695;286:676",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGArBYryu8xNlU="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGArBYryu8yVRc="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGArBYryu8zJHk="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGArBbN0/ToEwE=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGArBbN0/Tp/DY=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/ToEwE="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArBbN0/TqAyg=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tp/DY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 640,
+ "top": 448,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArBbN0/TrCUw=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tp/DY="
+ },
+ "font": "Arial;13;1",
+ "left": 685,
+ "top": 695,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "Bullet"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArBbN0/Ts1b4=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tp/DY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 640,
+ "top": 448,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArBbN0/Tt7M4=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tp/DY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 640,
+ "top": 448,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 680,
+ "top": 688,
+ "width": 299.40869140625,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGArBbN0/TqAyg="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGArBbN0/TrCUw="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGArBbN0/Ts1b4="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGArBbN0/Tt7M4="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGArBbN0/Tukyg=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/ToEwE="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBiWNAhQbJo=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBiWGQg7NCg="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 718,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+rect: Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBjXSQkdgmw=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBjXLwkIRBI="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 733,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+hit_rect: Rect",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBkIpAoOAOQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBkIfwn5Kug="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 748,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+speed: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBkvswr/fwQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBkvkArqIVs="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 763,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+_no: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBlZ9wvwlmk=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBlZ4AvbJ9g="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 778,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+rot: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBmAGgzh+lA=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBl/+wzMK0o="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 793,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+move: dict",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArBxLFA9wbgw=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBxK+Q9blYo="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 808,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+angle: float",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 680,
+ "top": 713,
+ "width": 299.40869140625,
+ "height": 113
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGArBbN0/Tvhms=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/ToEwE="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArJ+Euig5Cd4=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tvhms="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJ+EoygkofU="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 831,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "+update()",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArPrWqTgzckI=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tvhms="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPrWkTgeCkg="
+ },
+ "font": "Arial;13;0",
+ "left": 685,
+ "top": 846,
+ "width": 289.40869140625,
+ "height": 13,
+ "text": "-__init__(player_no: int, center: tuple, rot: int)",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 680,
+ "top": 826,
+ "width": 299.40869140625,
+ "height": 38
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGArBbN0/TwC58=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/ToEwE="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 320,
+ "top": 224,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGArBbN0/Tx1D0=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/ToEwE="
+ },
+ "model": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 320,
+ "top": 224,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 680,
+ "top": 688,
+ "width": 299.40869140625,
+ "height": 176,
+ "nameCompartment": {
+ "$ref": "AAAAAAGArBbN0/Tp/DY="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGArBbN0/Tukyg="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGArBbN0/Tvhms="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGArBbN0/TwC58="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGArBbN0/Tx1D0="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGArJb5pRQtpV8=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGArJb5pRQu4Oo=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQtpV8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArJb5pRQvqI8=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQu4Oo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -198.521484375,
+ "top": 496,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArJb5pRQwMY0=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQu4Oo="
+ },
+ "font": "Arial;13;1",
+ "left": 309,
+ "top": 703,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "Station"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArJb5pRQxhWk=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQu4Oo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -198.521484375,
+ "top": 496,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGArJb5pRQyy/s=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQu4Oo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -198.521484375,
+ "top": 496,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 304,
+ "top": 696,
+ "width": 351.43408203125,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGArJb5pRQvqI8="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGArJb5pRQwMY0="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGArJb5pRQxhWk="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGArJb5pRQyy/s="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGArJb5pRQzieg=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQtpV8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "subViews": [
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArJdt4h8+3Zo=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJdtyx8pIKU="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 726,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "#_no: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArJfR2CGGGs8=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJfRwCFx2g8="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 741,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "+count_frame: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArJgKnyJ3wLk=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJgKhyJi2sQ="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 756,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "+power: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArPiEQyqkZKk=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPiEKSqP6ng="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 771,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "#_id: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArPjBOiuWTlM=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPjBISuBBXg="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 786,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "+count_frame: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArPkGryyHjvQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPkGlixyI9Y="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 801,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "+capacity: int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLAttributeView",
+ "_id": "AAAAAAGArPlEWC50cXk=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPlELC5fJfA="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 816,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "+cool_down: int",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 304,
+ "top": 721,
+ "width": 351.43408203125,
+ "height": 113
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGArJb5pRQ0OOg=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQtpV8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArJhdoyUGko0=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQ0OOg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJhdeyTxoTk="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 839,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "+get_power(): int",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGArPmCojArTus=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQ0OOg="
+ },
+ "model": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "font": "Arial;13;0",
+ "left": 309,
+ "top": 854,
+ "width": 341.43408203125,
+ "height": 13,
+ "text": "-__init__(_id: int, _no: int, x: int, y: int, width: int, height: int)",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 304,
+ "top": 834,
+ "width": 351.43408203125,
+ "height": 38
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGArJb5pRQ1yLk=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQtpV8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -99.2607421875,
+ "top": 248,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGArJb5pRQ2IAA=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pRQtpV8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": -99.2607421875,
+ "top": 248,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 304,
+ "top": 696,
+ "width": 351.43408203125,
+ "height": 176,
+ "nameCompartment": {
+ "$ref": "AAAAAAGArJb5pRQu4Oo="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGArJb5pRQzieg="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGArJb5pRQ0OOg="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGArJb5pRQ1yLk="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGArJb5pRQ2IAA="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGArJb8MxSw3ZY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb8MhSuGlc="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArJb8MxSxUas=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb8MxSw3ZY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb8MhSuGlc="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 400,
+ "top": 687,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGArJb8MxSw3ZY="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArJb8MxSyei0=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb8MxSw3ZY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb8MhSuGlc="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 388,
+ "top": 695,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGArJb8MxSw3ZY="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArJb8MxSzWL8=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb8MxSw3ZY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArJb8MhSuGlc="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 425,
+ "top": 670,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGArJb8MxSw3ZY="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "tail": {
+ "$ref": "AAAAAAGArJb5pRQtpV8="
+ },
+ "lineStyle": 1,
+ "points": "420:695;407:676",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGArJb8MxSxUas="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGArJb8MxSyei0="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGArJb8MxSzWL8="
+ }
+ },
+ {
+ "_type": "UMLDependencyView",
+ "_id": "AAAAAAGArRJpHmtvCes=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArRJpHmtwCyE=",
+ "_parent": {
+ "$ref": "AAAAAAGArRJpHmtvCes="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 312,
+ "top": 323,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGArRJpHmtvCes="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArRJpHmtxV6I=",
+ "_parent": {
+ "$ref": "AAAAAAGArRJpHmtvCes="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 312,
+ "top": 308,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGArRJpHmtvCes="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGArRJpHmty1a4=",
+ "_parent": {
+ "$ref": "AAAAAAGArRJpHmtvCes="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 312,
+ "top": 353,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGArRJpHmtvCes="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "points": "312:308;312:344;360:344",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGArRJpHmtwCyE="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGArRJpHmtxV6I="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGArRJpHmty1a4="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAt2B/j8sCBrY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAt2B/j8sDh1w=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2B/j8sEuwk=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sDh1w="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 464,
+ "top": -40,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2B/kMsFs+A=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sDh1w="
+ },
+ "font": "Arial;13;1",
+ "left": 261,
+ "top": 271,
+ "width": 55.376953125,
+ "height": 13,
+ "text": "Game"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2B/kMsGFZ4=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sDh1w="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 464,
+ "top": -40,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2B/kMsHijc=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sDh1w="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 464,
+ "top": -40,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 256,
+ "top": 264,
+ "width": 65.376953125,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAt2B/j8sEuwk="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAt2B/kMsFs+A="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAt2B/kMsGFZ4="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAt2B/kMsHijc="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAt2B/kMsIO4k=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "font": "Arial;13;0",
+ "left": 256,
+ "top": 289,
+ "width": 65.376953125,
+ "height": 10
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAt2B/kMsJS6Y=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "font": "Arial;13;0",
+ "left": 256,
+ "top": 299,
+ "width": 65.376953125,
+ "height": 10
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAt2B/kMsKJ+s=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 232,
+ "top": -20,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAt2B/kMsLB88=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 232,
+ "top": -20,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 256,
+ "top": 264,
+ "width": 65.376953125,
+ "height": 45,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAt2B/j8sDh1w="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAt2B/kMsIO4k="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAt2B/kMsJS6Y="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAt2B/kMsKJ+s="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAt2B/kMsLB88="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGAt2CDMMt2ZWE=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2CDMMt0Z9w="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAt2CDMMt3Kec=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2CDMMt2ZWE="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2CDMMt0Z9w="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 246,
+ "top": 240,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAt2CDMMt2ZWE="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAt2CDMct4NTs=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2CDMMt2ZWE="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2CDMMt0Z9w="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 233,
+ "top": 248,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAt2CDMMt2ZWE="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAt2CDMct5uM8=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2CDMMt2ZWE="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2CDMMt0Z9w="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 271,
+ "top": 225,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAt2CDMMt2ZWE="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAk4i0r6t962o="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAt2B/j8sCBrY="
+ },
+ "lineStyle": 1,
+ "points": "274:263;245:215",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGAt2CDMMt3Kec="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAt2CDMct4NTs="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAt2CDMct5uM8="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAt2yrkdd/S50=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAt2yrkdeAgTs=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2yrkdeBr7Y=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdeAgTs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 704,
+ "top": -986,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2yrkdeCijs=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdeAgTs="
+ },
+ "font": "Arial;13;1",
+ "left": 605,
+ "top": 23,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "SoundControler"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2yrkdeDCKI=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdeAgTs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 704,
+ "top": -986,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAt2yrkdeEDYI=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdeAgTs="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 704,
+ "top": -986,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 600,
+ "top": 16,
+ "width": 404.18310546875,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAt2yrkdeBr7Y="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAt2yrkdeCijs="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAt2yrkdeDCKI="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAt2yrkdeEDYI="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAt2yrkdeFILA=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "font": "Arial;13;0",
+ "left": 600,
+ "top": 41,
+ "width": 404.18310546875,
+ "height": 10
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAt2yrkdeGDUM=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "subViews": [
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAt21f4ujnQ7g=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdeGDUM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt21fvOjVin0="
+ },
+ "font": "Arial;13;0",
+ "left": 605,
+ "top": 56,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+player_music(music_path: Str, volume: Float): None",
+ "horizontalAlignment": 0
+ },
+ {
+ "_type": "UMLOperationView",
+ "_id": "AAAAAAGAt22r4em0kdw=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdeGDUM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt22rxemimug="
+ },
+ "font": "Arial;13;0",
+ "left": 605,
+ "top": 71,
+ "width": 394.18310546875,
+ "height": 13,
+ "text": "+player_sound(sound_path: Str, volume: Float, max_time: Int): None",
+ "horizontalAlignment": 0
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 600,
+ "top": 51,
+ "width": 404.18310546875,
+ "height": 38
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAt2yrkdeHO1k=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 352,
+ "top": -493,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAt2yrkdeIQ+8=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "model": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 352,
+ "top": -493,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 600,
+ "top": 16,
+ "width": 404.18310546875,
+ "height": 73,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAt2yrkdeAgTs="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAt2yrkdeFILA="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAt2yrkdeGDUM="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAt2yrkdeHO1k="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAt2yrkdeIQ+8="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGAux1Tbu7gIiM=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGAux1Tbu7hkco=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAux1Tbu7iqmA=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7hkco="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 97.4150390625,
+ "top": -64,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAux1Tb+7jxoQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7hkco="
+ },
+ "font": "Arial;13;1",
+ "left": 821,
+ "top": 359,
+ "width": 70.05908203125,
+ "height": 13,
+ "text": "BattleMode"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAux1Tb+7kIqY=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7hkco="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 97.4150390625,
+ "top": -64,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGAux1Tb+7lW9A=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7hkco="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 97.4150390625,
+ "top": -64,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 816,
+ "top": 352,
+ "width": 80.05908203125,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAux1Tbu7iqmA="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGAux1Tb+7jxoQ="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGAux1Tb+7kIqY="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAux1Tb+7lW9A="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGAux1Tb+7mkiQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "font": "Arial;13;0",
+ "left": 816,
+ "top": 377,
+ "width": 80.05908203125,
+ "height": 10
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGAux1Tb+7nlpE=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "font": "Arial;13;0",
+ "left": 816,
+ "top": 387,
+ "width": 80.05908203125,
+ "height": 10
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGAux1Tb+7oIbk=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 48.70751953125,
+ "top": -32,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGAux1Tb+7peBE=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 48.70751953125,
+ "top": -32,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 816,
+ "top": 352,
+ "width": 80.05908203125,
+ "height": 45,
+ "nameCompartment": {
+ "$ref": "AAAAAAGAux1Tbu7hkco="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGAux1Tb+7mkiQ="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGAux1Tb+7nlpE="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGAux1Tb+7oIbk="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGAux1Tb+7peBE="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGAux1WsO9jHLk=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Wr+9hCM4="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAux1WsO9kFRQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1WsO9jHLk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Wr+9hCM4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 783,
+ "top": 353,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAux1WsO9jHLk="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAux1WsO9l69Y=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1WsO9jHLk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Wr+9hCM4="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 777,
+ "top": 367,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGAux1WsO9jHLk="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGAux1WsO9mhi4=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1WsO9jHLk="
+ },
+ "model": {
+ "$ref": "AAAAAAGAux1Wr+9hCM4="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 794,
+ "top": 326,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGAux1WsO9jHLk="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "lineStyle": 1,
+ "points": "815:357;764:336",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGAux1WsO9kFRQ="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGAux1WsO9l69Y="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGAux1WsO9mhi4="
+ }
+ },
+ {
+ "_type": "UMLDependencyView",
+ "_id": "AAAAAAGA2rKzngQloLo=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rKzngQmj90=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rKzngQloLo="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 902,
+ "top": 233,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rKzngQloLo="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rKzngQnQVo=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rKzngQloLo="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 917,
+ "top": 233,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rKzngQloLo="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rKzngQoFJY=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rKzngQloLo="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 872,
+ "top": 234,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rKzngQloLo="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAux1Tbu7gIiM="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAl12L1ka5A8Q="
+ },
+ "points": "1024:240;887:240;887:352",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGA2rKzngQmj90="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2rKzngQnQVo="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2rKzngQoFJY="
+ }
+ },
+ {
+ "_type": "UMLDependencyView",
+ "_id": "AAAAAAGA2rOQ8REXCjk=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rOQ8REYFRw=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rOQ8REXCjk="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 296,
+ "top": 531,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rOQ8REXCjk="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rOQ8REZpO0=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rOQ8REXCjk="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 296,
+ "top": 516,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rOQ8REXCjk="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rOQ8REauMs=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rOQ8REXCjk="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 296,
+ "top": 561,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rOQ8REXCjk="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAl441S8CcU7A="
+ },
+ "points": "296:560;296:552;576:552",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGA2rOQ8REYFRw="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2rOQ8REZpO0="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2rOQ8REauMs="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGA2rRtGhaVeJg=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGA2rRtGhaW0l8=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2rRtGhaX2KU=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaW0l8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 336,
+ "top": -120,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2rRtGxaYLvE=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaW0l8="
+ },
+ "font": "Arial;13;1",
+ "left": 853,
+ "top": 535,
+ "width": 41.919921875,
+ "height": 13,
+ "text": "Bullet"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2rRtGxaZs6E=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaW0l8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 336,
+ "top": -120,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2rRtGxaatfQ=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaW0l8="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 336,
+ "top": -120,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 848,
+ "top": 528,
+ "width": 51.919921875,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2rRtGhaX2KU="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGA2rRtGxaYLvE="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGA2rRtGxaZs6E="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2rRtGxaatfQ="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGA2rRtGxabAsU=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ },
+ "font": "Arial;13;0",
+ "left": 848,
+ "top": 553,
+ "width": 51.919921875,
+ "height": 10
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGA2rRtGxacCOU=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ },
+ "font": "Arial;13;0",
+ "left": 848,
+ "top": 563,
+ "width": 51.919921875,
+ "height": 10
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGA2rRtGxad/Q8=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 168,
+ "top": -60,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGA2rRtGxaeC6g=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 168,
+ "top": -60,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 848,
+ "top": 528,
+ "width": 51.919921875,
+ "height": 45,
+ "nameCompartment": {
+ "$ref": "AAAAAAGA2rRtGhaW0l8="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGA2rRtGxabAsU="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGA2rRtGxacCOU="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGA2rRtGxad/Q8="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGA2rRtGxaeC6g="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGA2rRwnxbrA3w=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRwnxbpX48="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rRwoBbsuqk=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRwnxbrA3w="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRwnxbpX48="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 842,
+ "top": 620,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rRwnxbrA3w="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rRwoBbtwRc=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRwnxbrA3w="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRwnxbpX48="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 827,
+ "top": 617,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rRwnxbrA3w="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rRwoBbuetw=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rRwnxbrA3w="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2rRwnxbpX48="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 871,
+ "top": 627,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rRwnxbrA3w="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "tail": {
+ "$ref": "AAAAAAGArBbN0/ToEwE="
+ },
+ "lineStyle": 1,
+ "points": "846:687;869:573",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGA2rRwoBbsuqk="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2rRwoBbtwRc="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2rRwoBbuetw="
+ }
+ },
+ {
+ "_type": "UMLDependencyView",
+ "_id": "AAAAAAGA2rTdbxm5yuo=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rTdbxm6lGE=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rTdbxm5yuo="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 880,
+ "top": 505,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rTdbxm5yuo="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rTdbxm79h8=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rTdbxm5yuo="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 880,
+ "top": 520,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rTdbxm5yuo="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2rTdbxm8i5w=",
+ "_parent": {
+ "$ref": "AAAAAAGA2rTdbxm5yuo="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 881,
+ "top": 475,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2rTdbxm5yuo="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAk4Bm7aZpz/0="
+ },
+ "tail": {
+ "$ref": "AAAAAAGA2rRtGhaVeJg="
+ },
+ "points": "881:528;881:496;765:496",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGA2rTdbxm6lGE="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2rTdbxm79h8="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2rTdbxm8i5w="
+ }
+ },
+ {
+ "_type": "UMLDependencyView",
+ "_id": "AAAAAAGA2r5nhyxk8B8=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2r5nhyxlR7o=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r5nhyxk8B8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 454,
+ "top": 41,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2r5nhyxk8B8="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2r5nhyxmqqI=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r5nhyxk8B8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 469,
+ "top": 41,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2r5nhyxk8B8="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2r5nhyxn+Pc=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r5nhyxk8B8="
+ },
+ "model": {
+ "$ref": "AAAAAAGArRJpHWtt8EM="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 425,
+ "top": 42,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2r5nhyxk8B8="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAXjxqhKVg3/0="
+ },
+ "tail": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "points": "600:48;440:48;440:96",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGA2r5nhyxlR7o="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2r5nhyxmqqI="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2r5nhyxn+Pc="
+ }
+ },
+ {
+ "_type": "UMLClassView",
+ "_id": "AAAAAAGA2r8tmS9PRnY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "subViews": [
+ {
+ "_type": "UMLNameCompartmentView",
+ "_id": "AAAAAAGA2r8tmS9QQ5o=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9PRnY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "subViews": [
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2r8tmS9RiAU=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9QQ5o="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 400,
+ "top": -60,
+ "height": 13
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2r8tmS9SdB8=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9QQ5o="
+ },
+ "font": "Arial;13;1",
+ "left": 805,
+ "top": 135,
+ "width": 128.3115234375,
+ "height": 13,
+ "text": "TankSoundControler"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2r8tmS9THCA=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9QQ5o="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 400,
+ "top": -60,
+ "width": 73.67724609375,
+ "height": 13,
+ "text": "(from Model)"
+ },
+ {
+ "_type": "LabelView",
+ "_id": "AAAAAAGA2r8tmS9UZfo=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9QQ5o="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 400,
+ "top": -60,
+ "height": 13,
+ "horizontalAlignment": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "left": 800,
+ "top": 128,
+ "width": 138.3115234375,
+ "height": 25,
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2r8tmS9RiAU="
+ },
+ "nameLabel": {
+ "$ref": "AAAAAAGA2r8tmS9SdB8="
+ },
+ "namespaceLabel": {
+ "$ref": "AAAAAAGA2r8tmS9THCA="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2r8tmS9UZfo="
+ }
+ },
+ {
+ "_type": "UMLAttributeCompartmentView",
+ "_id": "AAAAAAGA2r8tmS9VnwQ=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9PRnY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "font": "Arial;13;0",
+ "left": 800,
+ "top": 153,
+ "width": 138.3115234375,
+ "height": 10
+ },
+ {
+ "_type": "UMLOperationCompartmentView",
+ "_id": "AAAAAAGA2r8tmS9WUE8=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9PRnY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "font": "Arial;13;0",
+ "left": 800,
+ "top": 163,
+ "width": 138.3115234375,
+ "height": 10
+ },
+ {
+ "_type": "UMLReceptionCompartmentView",
+ "_id": "AAAAAAGA2r8tmS9XWhw=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9PRnY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 200,
+ "top": -30,
+ "width": 10,
+ "height": 10
+ },
+ {
+ "_type": "UMLTemplateParameterCompartmentView",
+ "_id": "AAAAAAGA2r8tmS9YpNY=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmS9PRnY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 200,
+ "top": -30,
+ "width": 10,
+ "height": 10
+ }
+ ],
+ "font": "Arial;13;0",
+ "containerChangeable": true,
+ "left": 800,
+ "top": 128,
+ "width": 138.3115234375,
+ "height": 45,
+ "nameCompartment": {
+ "$ref": "AAAAAAGA2r8tmS9QQ5o="
+ },
+ "attributeCompartment": {
+ "$ref": "AAAAAAGA2r8tmS9VnwQ="
+ },
+ "operationCompartment": {
+ "$ref": "AAAAAAGA2r8tmS9WUE8="
+ },
+ "receptionCompartment": {
+ "$ref": "AAAAAAGA2r8tmS9XWhw="
+ },
+ "templateParameterCompartment": {
+ "$ref": "AAAAAAGA2r8tmS9YpNY="
+ }
+ },
+ {
+ "_type": "UMLGeneralizationView",
+ "_id": "AAAAAAGA2r8xtC+HpCc=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBtyKM79qY="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8xtC+FTUY="
+ },
+ "subViews": [
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2r8xtC+Igus=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8xtC+HpCc="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8xtC+FTUY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 826,
+ "top": 110,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2r8xtC+HpCc="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2r8xtC+JLmY=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8xtC+HpCc="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8xtC+FTUY="
+ },
+ "visible": null,
+ "font": "Arial;13;0",
+ "left": 814,
+ "top": 118,
+ "height": 13,
+ "alpha": 1.5707963267948966,
+ "distance": 30,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2r8xtC+HpCc="
+ },
+ "edgePosition": 1
+ },
+ {
+ "_type": "EdgeLabelView",
+ "_id": "AAAAAAGA2r8xtC+KvpY=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8xtC+HpCc="
+ },
+ "model": {
+ "$ref": "AAAAAAGA2r8xtC+FTUY="
+ },
+ "visible": false,
+ "font": "Arial;13;0",
+ "left": 851,
+ "top": 93,
+ "height": 13,
+ "alpha": -1.5707963267948966,
+ "distance": 15,
+ "hostEdge": {
+ "$ref": "AAAAAAGA2r8xtC+HpCc="
+ },
+ "edgePosition": 1
+ }
+ ],
+ "font": "Arial;13;0",
+ "head": {
+ "$ref": "AAAAAAGAt2yrkdd/S50="
+ },
+ "tail": {
+ "$ref": "AAAAAAGA2r8tmS9PRnY="
+ },
+ "lineStyle": 1,
+ "points": "852:127;826:89",
+ "showVisibility": true,
+ "nameLabel": {
+ "$ref": "AAAAAAGA2r8xtC+Igus="
+ },
+ "stereotypeLabel": {
+ "$ref": "AAAAAAGA2r8xtC+JLmY="
+ },
+ "propertyLabel": {
+ "$ref": "AAAAAAGA2r8xtC+KvpY="
+ }
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAXjxqgqVefzo=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "GameMode",
+ "ownedElements": [
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAk4BnNKaQwu4=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk4BnNKaRqgE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4BnNKaQwu4="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk4BnNKaS5fU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4BnNKaQwu4="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ }
+ }
+ },
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAk4i1pKuzgtg=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "source": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "target": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAk49dULqoMoU=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk49dULqprL4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk49dULqoMoU="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk49dULqqDTk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk49dULqoMoU="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAk6cYod/xDsc=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk6cYod/yqTQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6cYod/xDsc="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk6cYod/zYlk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6cYod/xDsc="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk6cYDd+5W4E="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAk6eoNOFRg2c=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk6eoNOFSc58=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6eoNOFRg2c="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk6eoNOFTh3E=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6eoNOFRg2c="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk6enhOEK5m0="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAlv9NBkqd3Qg=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAlv9NBkqehx4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9NBkqd3Qg="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAlv9NBkqfwcg=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9NBkqd3Qg="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAlw1bZIsuXy4=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAlw1bZIsvXas=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1bZIsuXy4="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAlw1bZIsw4Ks=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1bZIsuXy4="
+ },
+ "reference": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAl57vqBbm3Ro=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl57vqBbn1Y8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqBbm3Ro="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl57vqBbo+Ck=",
+ "_parent": {
+ "$ref": "AAAAAAGAl57vqBbm3Ro="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAt2yu69fxX14=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAt2yu69fyJ1s=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yu69fxX14="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAt2yu69fzZFY=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yu69fxX14="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ }
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk0r8lqXMu2M=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "all_sprites",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAXj2LaKWObGk=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "bullets",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk3nQ0KYmHrg=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "walls",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk3yvZqZCZa4=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "mobs",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk28mGKYPh40=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "player_1P",
+ "type": "Sprite"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk2WvwaYITAc=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "is_paused",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk3fk+KYW84M=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "is_playing",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk3hGkKYe19M=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "is_sound",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk2Un6KYBvzo=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "used_frame",
+ "type": "Int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmBTs57dfR+A=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "frame_limit",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk3qQUqYtB2U=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "window",
+ "type": "pygame.Surface"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl1t4D0O8V40=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "map",
+ "type": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ }
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmAyRvVDTY88=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "state",
+ "type": "Str"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmAzqn1QpDMk=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "status",
+ "type": "str"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk15Ly6XULl4=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk4MoKaeAjcY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk15Ly6XULl4="
+ },
+ "name": "map_path",
+ "type": "Str"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk4MoL6eBWV4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk15Ly6XULl4="
+ },
+ "name": "time_limit",
+ "type": "Int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk4P1uKeR7m4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk15Ly6XULl4="
+ },
+ "name": "sound",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk7kYswkcTw4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk15Ly6XULl4="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk1709KXc5+4=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "get_result",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk7lJigl/D5A=",
+ "_parent": {
+ "$ref": "AAAAAAGAk1709KXc5+4="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk1894KXkSJ8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "updata",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk4STn6i5rCw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk1894KXkSJ8="
+ },
+ "name": "command",
+ "type": "Dict"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk7ll4wni+6E=",
+ "_parent": {
+ "$ref": "AAAAAAGAk1894KXkSJ8="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk2Bn7qXt7o8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "check_events",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk7l76gpF308=",
+ "_parent": {
+ "$ref": "AAAAAAGAk2Bn7qXt7o8="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk2DZMqX0aQQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "check_collisions",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk7mjUQqoCa4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk2DZMqX0aQQ="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAmBa4jbpw4xo=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "reset",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAmBkSOr8wht8=",
+ "_parent": {
+ "$ref": "AAAAAAGAmBa4jbpw4xo="
+ },
+ "type": "tuple",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAtcd3+p02Ti8=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "draw_sprite_data"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAtcf9QKFmM8o=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "create_init_image_data"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAtcgr7KVa4xs=",
+ "_parent": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "name": "draw_text_data"
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAXlNOraWdYtA=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "BattleMode"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk4Bm7aZnfh4=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "BattleMode",
+ "ownedElements": [
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAl442pcD/AMA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl442pcEA4c0=",
+ "_parent": {
+ "$ref": "AAAAAAGAl442pcD/AMA="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "aggregation": "shared"
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl442pcEBQxY=",
+ "_parent": {
+ "$ref": "AAAAAAGAl442pcD/AMA="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ }
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk4VFJajrfOU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "player_2P",
+ "type": "sprite"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl1ODLSYTr98=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "is_invincible",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl1PKaSpLQKM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "is_through_wall",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl1aZlTOYSFU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "is_debug",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmCsj48vzQzY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "players",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmCtP1szA3Lk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "walls",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmCt9Oc2xkSk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "bullets",
+ "type": "Group"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmCu0Q86iDIA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "player_1P",
+ "type": "sprite"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAq751PONGLwk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "create_bullte",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAq79ZKePvrbE=",
+ "_parent": {
+ "$ref": "AAAAAAGAq751PONGLwk="
+ },
+ "name": "shoot_info",
+ "type": "Dict"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAtepAErCzCDo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "draw_sprite_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAtep5lrFB8L4=",
+ "_parent": {
+ "$ref": "AAAAAAGAtepAErCzCDo="
+ },
+ "type": "list",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAtep9JrFwneU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "create_init_image_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAteq0S7Icles=",
+ "_parent": {
+ "$ref": "AAAAAAGAtep9JrFwneU="
+ },
+ "type": "list",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAteq3xbJLFFI=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "name": "draw_text_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAter5rrMzGxc=",
+ "_parent": {
+ "$ref": "AAAAAAGAteq3xbJLFFI="
+ },
+ "type": "list",
+ "direction": "return"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk4i0rqt7zy4=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "MyGame",
+ "ownedElements": [
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAk4y3WK4ALvo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk4y3WK4BatA=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4y3WK4ALvo="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ }
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAk4y3WK4ClmQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4y3WK4ALvo="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "aggregation": "shared"
+ }
+ },
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAk5DXmsJevOw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "source": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "target": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAl012qu/x7Ck=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl012qu/yLn4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl012qu/x7Ck="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ }
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl012qu/zDiE=",
+ "_parent": {
+ "$ref": "AAAAAAGAl012qu/x7Ck="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ }
+ }
+ },
+ {
+ "_type": "UMLAssociation",
+ "_id": "AAAAAAGAl1AtTfpwcuU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "end1": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl1AtTfpxYF0=",
+ "_parent": {
+ "$ref": "AAAAAAGAl1AtTfpwcuU="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ }
+ },
+ "end2": {
+ "_type": "UMLAssociationEnd",
+ "_id": "AAAAAAGAl1AtTfpyJbg=",
+ "_parent": {
+ "$ref": "AAAAAAGAl1AtTfpwcuU="
+ },
+ "reference": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ }
+ }
+ },
+ {
+ "_type": "UMLDependency",
+ "_id": "AAAAAAGArRJpHWtt8EM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "source": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "target": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlvY38TFLyd8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "scene",
+ "type": "Scene"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk4kf+KyVMfg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "attachements",
+ "type": "List"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk4l896y9t7w=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "game_mode",
+ "type": "Q"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk4qz3azr1dY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "sound",
+ "type": "Str"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk4uV6a0Zir8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "map_path",
+ "type": "Str"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlvcFRzJ2WKg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlvdZ9DKzID8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlvcFRzJ2WKg="
+ },
+ "name": "map_no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlvdZ9DK0nsQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAlvcFRzJ2WKg="
+ },
+ "name": "sound",
+ "type": "str"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5zhAtNMoaw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "get_scene_info",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6Gk29qFbgk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5zhAtNMoaw="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk52n3dPyZ8c=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "is_running",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6HPK9rxn3M=",
+ "_parent": {
+ "$ref": "AAAAAAGAk52n3dPyZ8c="
+ },
+ "type": "Bool",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk54nv9Q7Idw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "set_game_mode",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6JuY9u3Yeo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk54nv9Q7Idw="
+ },
+ "name": "game_path",
+ "type": "Str"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6JuY9u4GK4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk54nv9Q7Idw="
+ },
+ "type": "Q",
+ "direction": "return"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk7STMvrR3ow=",
+ "_parent": {
+ "$ref": "AAAAAAGAk54nv9Q7Idw="
+ },
+ "name": "is_sound",
+ "type": "Bool"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk6KdVtwW3yQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ },
+ "name": "rank",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6L7nt0lQpE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6KdVtwW3yQ="
+ },
+ "type": "List",
+ "direction": "return"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk4y2+q3IC28=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class1"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk49c4rpw7bU=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class2"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk5CJRb/kl10=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class3",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAk5CJqcAr7io=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5CJRb/kl10="
+ },
+ "source": {
+ "$ref": "AAAAAAGAk4Bm7aZnfh4="
+ },
+ "target": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ }
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk5DXD8IXyNM=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "PaiaGame",
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk5G7FsRFRTg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "scene",
+ "type": "Scene"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk5H9QsSOiRk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "frame_count",
+ "type": "Int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk5I3GsTjZv4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "game_result_state",
+ "type": "Str"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5W51cWqrZ4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5oEMcq8Yag=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5W51cWqrZ4="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5XwpMYA/gU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "update",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5kubsoOvlM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5XwpMYA/gU="
+ },
+ "type": "None",
+ "direction": "return"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5nihsplpqQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5XwpMYA/gU="
+ },
+ "name": "commands",
+ "type": "Dict"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5a2GMciKVk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "game_to_player_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5dAyMdrnfU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5a2GMciKVk="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5dBQsd/Tmw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "reset",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5doDMfImGU=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5dBQsd/Tmw="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5dojsfcCqM=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "get_scene_init_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5fDusgltxQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5dojsfcCqM="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5fEMsg5d4g=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "get_scene_progress_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5gh+8iCscw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5fEMsg5d4g="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5gs7ciWGr0=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "get_game_result",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5iCDcjfyUE=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5gs7ciWGr0="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5iCl8jzZ5c=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "get_keyboard_command",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5jVP8k8WZ8=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5iCl8jzZ5c="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk5jVvclQBvY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5DXD8IXyNM="
+ },
+ "name": "ai_clients",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk5kSCcmZHNw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk5jVvclQBvY="
+ },
+ "type": "List",
+ "direction": "return"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk6cYDd+5W4E=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "sound_controller"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk6enhOEK5m0=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "TankManMap",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAk6h3Z+igXLw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6enhOEK5m0="
+ },
+ "source": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ },
+ "target": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ }
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAk6h2wehKJfM=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "TiledMap",
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk6urfvGImRk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "width",
+ "type": "Int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk6vsvvHyyiw=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "height",
+ "type": "Int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAk6wt1vL1d7s=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "tmx_data",
+ "type": "pytmx.pytmx.TieldMap"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk6kr/O4CXBo=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "creat_img_init_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6n2B+9FaUQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6kr/O4CXBo="
+ },
+ "type": "List",
+ "direction": "return"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlvm10jeA4Yk=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6kr/O4CXBo="
+ },
+ "name": "img_no_list",
+ "type": "List"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk6l+y+5sE9E=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "creat_walls_init_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6nQkO7E5AY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6l+y+5sE9E="
+ },
+ "type": "List",
+ "direction": "return"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlvnlgDfXWRQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6l+y+5sE9E="
+ },
+ "name": "walls_img_no_list",
+ "type": "List"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAk6pg8fCVFEY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "creat_mobs_init_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAk6p/yPDt2MY=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6pg8fCVFEY="
+ },
+ "type": "List",
+ "direction": "return"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlvoTRzg6oN4=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6pg8fCVFEY="
+ },
+ "name": "mobs_img_no_list",
+ "type": "List"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArQUJgl6yVkg=",
+ "_parent": {
+ "$ref": "AAAAAAGAk6h2wehKJfM="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArQU2W19brcY=",
+ "_parent": {
+ "$ref": "AAAAAAGArQUJgl6yVkg="
+ },
+ "name": "map_no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArQU2XF9cNDk=",
+ "_parent": {
+ "$ref": "AAAAAAGArQUJgl6yVkg="
+ },
+ "name": "time_limit",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArQU2XF9dLBE=",
+ "_parent": {
+ "$ref": "AAAAAAGArQUJgl6yVkg="
+ },
+ "name": "sound",
+ "type": "str"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAlv9MxkpWtl4=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Player",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAl12MsEcchKs=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "source": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "target": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwJoZ1/tBu0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "img_id",
+ "type": "str"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwGUa13yup4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "is_shoot",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwMnN2KEupU=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "is_backward",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwOU8WMn6Wc=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "is_forward",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwPf6GeKsQk=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "last_shoot_fram",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwR9kGgtS2k=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "shoot_delay",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwTtomxhH4o=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "shield",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwdG1XGdV3I=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "rot",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwdqIHJA7mw=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "rot_speed",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwW7rW2h/mA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "move",
+ "type": "List"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwibfnTMDYs=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "surface",
+ "type": "Surface"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlwk/Nna1N4Q=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "Attribute1",
+ "type": ""
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArUoSY5Bgse4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "angle",
+ "type": "Float"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwn094AqVB0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlwoyW4CdgdM=",
+ "_parent": {
+ "$ref": "AAAAAAGAlwn094AqVB0="
+ },
+ "name": "_no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlwoyW4CeOJ0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlwn094AqVB0="
+ },
+ "name": "x",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlwoyW4CfOaQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAlwn094AqVB0="
+ },
+ "name": "y",
+ "type": "int"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwpCJoD4hM4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "update",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlwqLsYFrbHY=",
+ "_parent": {
+ "$ref": "AAAAAAGAlwpCJoD4hM4="
+ },
+ "name": "command",
+ "type": "Dic"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwqMMIGRwsA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "act",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAlws/R4LpU3o=",
+ "_parent": {
+ "$ref": "AAAAAAGAlwqMMIGRwsA="
+ },
+ "name": "commands",
+ "type": "Dic"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlws/04MPOTg=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "forward"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwthaYOyjnQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "backward"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwt+z4RV5c4=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "turn_left"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwuZCYT4X14=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "turn_right"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwwF3obhmr0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "collide_with_walls"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlwwt14eEiHY=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "collide_with_bullets"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl2lm1lFgub0=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "create_shoot_info",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArQzl6GLVb0E=",
+ "_parent": {
+ "$ref": "AAAAAAGAl2lm1lFgub0="
+ },
+ "type": "dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl2qJGlMLItA=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "rotate"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAq8OBs+ZVm28=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "create_shoot_info",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAq8QFS+mrae8=",
+ "_parent": {
+ "$ref": "AAAAAAGAq8OBs+ZVm28="
+ },
+ "type": "dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArUpRK5UXbhM=",
+ "_parent": {
+ "$ref": "AAAAAAGAlv9MxkpWtl4="
+ },
+ "name": "get_image_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArUqA/5Wl3/Y=",
+ "_parent": {
+ "$ref": "AAAAAAGArUpRK5UXbhM="
+ },
+ "type": "dict",
+ "direction": "return"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAlw1a9IrJsQE=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Obstacle",
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlw22UY7K4Ig=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "rect_ Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlw3Xno92lR8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "hit_rect",
+ "type": "Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlw4hA5BA2UQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "pos",
+ "type": "Vector2"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAlw748pRCH1Q=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "lives",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBgSKQYpZa8=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "_no",
+ "visibility": "protected",
+ "type": "int"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlw5+s5IJsbI=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "updata"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAlw67WJK1kyc=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "get_position",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl5ZQCvegTos=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw67WJK1kyc="
+ },
+ "type": "Tuple",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArBeO6QFjVkw=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "collide_with_bullets"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArP0eyUMoi6I=",
+ "_parent": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArP1KvUPRorI=",
+ "_parent": {
+ "$ref": "AAAAAAGArP0eyUMoi6I="
+ },
+ "name": "no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArP1KvUPSvJE=",
+ "_parent": {
+ "$ref": "AAAAAAGArP0eyUMoi6I="
+ },
+ "name": "x",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArP1KvkPT9fE=",
+ "_parent": {
+ "$ref": "AAAAAAGArP0eyUMoi6I="
+ },
+ "name": "y",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArP1KvkPUaes=",
+ "_parent": {
+ "$ref": "AAAAAAGArP0eyUMoi6I="
+ },
+ "name": "width",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArP1KvkPVPY8=",
+ "_parent": {
+ "$ref": "AAAAAAGArP0eyUMoi6I="
+ },
+ "name": "height",
+ "type": "int"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAl012Du99Xks=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class4"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAl1Asv/oLieY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class5"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAl12L1ka3Ntc=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Player",
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl3x7tYZrEgc=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "hit_rect",
+ "type": "pygame.Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl31Nc4jZfC8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "img_path",
+ "type": "str"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl34Jtou/xeM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "player_info",
+ "type": "Dict"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl3/i6ZX1W/I=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "result_info",
+ "type": "Dict"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4FjaZjP9Js=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "pos",
+ "type": "Vector2"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4L7QJu261g=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "rect",
+ "type": "pygame.Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4VhZ53EuA8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "score",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4Wdu6AacQY=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "speed",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4hBwaQ2YAk=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "used_frame",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4gFNKIok+Q=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "is_alive",
+ "type": "Bool"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4h44qaYN9Y=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "vel",
+ "type": "Vector2"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAmGGmHNceHdk=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "_no",
+ "visibility": "protected",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArT+nLob0qfY=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "image_data",
+ "type": "dict"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4kQlqn3uu8=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "update",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl4kbWapqdIo=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4kQlqn3uu8="
+ },
+ "name": "command",
+ "type": "Dic"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4qWLbKs5LA=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "act",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl4qe0bMfDWw=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4qWLbKs5LA="
+ },
+ "name": "commands",
+ "type": "Dic"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4nZw6wTnwM=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "get_info",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl4npN6yGzBQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4nZw6wTnwM="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4onB65HF+4=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "get_result",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl4ouRq66AiQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4onB65HF+4="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4wLGLXqvNE=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "get_position",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl40Oyrqv7II=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4wLGLXqvNE="
+ },
+ "type": "Tuple",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4x9obg0ows=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "player_init_data",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl4zmhLmk57E=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4x9obg0ows="
+ },
+ "type": "Dict",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArT0E5nwq/LE=",
+ "_parent": {
+ "$ref": "AAAAAAGAl12L1ka3Ntc="
+ },
+ "name": "get_image_data"
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAl441S8CaKhY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Prop",
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl48EYsUZjkQ=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "name": "rect",
+ "type": "pygame.Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGAl4+pJsmHF4I=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "name": "hit_rect",
+ "type": "Rect"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl47IncNQv5A=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "name": "update"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAl4/eCsuMa9A=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "name": "get_position",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGAl5aFX/m/9TU=",
+ "_parent": {
+ "$ref": "AAAAAAGAl4/eCsuMa9A="
+ },
+ "type": "Tuple",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArPxSYkB9Khg=",
+ "_parent": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPyb7UEmpVA=",
+ "_parent": {
+ "$ref": "AAAAAAGArPxSYkB9Khg="
+ },
+ "name": "x",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPyb7UEnlnM=",
+ "_parent": {
+ "$ref": "AAAAAAGArPxSYkB9Khg="
+ },
+ "name": "y",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPyb7UEovWE=",
+ "_parent": {
+ "$ref": "AAAAAAGArPxSYkB9Khg="
+ },
+ "name": "width",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPyb7kEp7wo=",
+ "_parent": {
+ "$ref": "AAAAAAGArPxSYkB9Khg="
+ },
+ "name": "height",
+ "type": "int"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAl57tthZyB50=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class6"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGArBYpZe6rwvo=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Class7",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGArBYrye8uD2Q=",
+ "_parent": {
+ "$ref": "AAAAAAGArBYpZe6rwvo="
+ },
+ "source": {
+ "$ref": "AAAAAAGAlw1a9IrJsQE="
+ },
+ "target": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ }
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGArBbN0/Tmgq0=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Bullet",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGA2rRwnxbpX48=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "source": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "target": {
+ "$ref": "AAAAAAGA2rRtGRaTuHc="
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBiWGQg7NCg=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "rect",
+ "type": "Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBjXLwkIRBI=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "hit_rect",
+ "type": "Rect"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBkIfwn5Kug=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "speed",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBkvkArqIVs=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "_no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBlZ4AvbJ9g=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "rot",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBl/+wzMK0o=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "move",
+ "type": "dict"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArBxK+Q9blYo=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "angle",
+ "type": "float"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArJ+EoygkofU=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "update"
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArPrWkTgeCkg=",
+ "_parent": {
+ "$ref": "AAAAAAGArBbN0/Tmgq0="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPtKPjjHQ/c=",
+ "_parent": {
+ "$ref": "AAAAAAGArPrWkTgeCkg="
+ },
+ "name": "player_no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPtKPjjIRkQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArPrWkTgeCkg="
+ },
+ "name": "center",
+ "type": "tuple"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPtKPjjJGgA=",
+ "_parent": {
+ "$ref": "AAAAAAGArPrWkTgeCkg="
+ },
+ "name": "rot",
+ "type": "int"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGArJb5pBQr9bw=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Station",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGArJb8MhSuGlc=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "source": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "target": {
+ "$ref": "AAAAAAGAl441S8CaKhY="
+ }
+ }
+ ],
+ "attributes": [
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArJdtyx8pIKU=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "_no",
+ "visibility": "protected",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArJfRwCFx2g8=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "count_frame",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArJgKhyJi2sQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "power",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArPiEKSqP6ng=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "_id",
+ "visibility": "protected",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArPjBISuBBXg=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "count_frame",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArPkGlixyI9Y=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "capacity",
+ "type": "int"
+ },
+ {
+ "_type": "UMLAttribute",
+ "_id": "AAAAAAGArPlELC5fJfA=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "cool_down",
+ "type": "int"
+ }
+ ],
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArJhdeyTxoTk=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "get_power",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArJiOeSWasDM=",
+ "_parent": {
+ "$ref": "AAAAAAGArJhdeyTxoTk="
+ },
+ "type": "int",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGArPmCiTAWIYM=",
+ "_parent": {
+ "$ref": "AAAAAAGArJb5pBQr9bw="
+ },
+ "name": "__init__",
+ "visibility": "private",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPod3zC/3HQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "name": "_id",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPod3zDAUwE=",
+ "_parent": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "name": "_no",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPod3zDBBXQ=",
+ "_parent": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "name": "x",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPod4DDCjc0=",
+ "_parent": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "name": "y",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPod4DDDrXw=",
+ "_parent": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "name": "width",
+ "type": "int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGArPod4DDEFA4=",
+ "_parent": {
+ "$ref": "AAAAAAGArPmCiTAWIYM="
+ },
+ "name": "height",
+ "type": "int"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAt2B/jcsAIYY=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Game",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAt2CDMMt0Z9w=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "source": {
+ "$ref": "AAAAAAGAt2B/jcsAIYY="
+ },
+ "target": {
+ "$ref": "AAAAAAGAk4i0rqt7zy4="
+ }
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAt2yrkNd9c4Q=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "SoundControler",
+ "operations": [
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAt21fvOjVin0=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "name": "player_music",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2ren+R8sJls=",
+ "_parent": {
+ "$ref": "AAAAAAGAt21fvOjVin0="
+ },
+ "name": "music_path",
+ "type": "Str"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2ren+R8trQU=",
+ "_parent": {
+ "$ref": "AAAAAAGAt21fvOjVin0="
+ },
+ "name": "volume",
+ "type": "Float"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2ren+R8uSeE=",
+ "_parent": {
+ "$ref": "AAAAAAGAt21fvOjVin0="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ },
+ {
+ "_type": "UMLOperation",
+ "_id": "AAAAAAGAt22rxemimug=",
+ "_parent": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ },
+ "name": "player_sound",
+ "parameters": [
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2rdVuR3THQE=",
+ "_parent": {
+ "$ref": "AAAAAAGAt22rxemimug="
+ },
+ "name": "sound_path",
+ "type": "Str"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2rdVuR3UIQw=",
+ "_parent": {
+ "$ref": "AAAAAAGAt22rxemimug="
+ },
+ "name": "volume",
+ "type": "Float"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2rdVuh3VfZM=",
+ "_parent": {
+ "$ref": "AAAAAAGAt22rxemimug="
+ },
+ "name": "max_time",
+ "type": "Int"
+ },
+ {
+ "_type": "UMLParameter",
+ "_id": "AAAAAAGA2rdVuh3Wa3s=",
+ "_parent": {
+ "$ref": "AAAAAAGAt22rxemimug="
+ },
+ "type": "None",
+ "direction": "return"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGAux1Tbe7ewXU=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "BattleMode",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGAux1Wr+9hCM4=",
+ "_parent": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "source": {
+ "$ref": "AAAAAAGAux1Tbe7ewXU="
+ },
+ "target": {
+ "$ref": "AAAAAAGAXjxqgqVefzo="
+ }
+ }
+ ]
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGA2rRtGRaTuHc=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "Bullet"
+ },
+ {
+ "_type": "UMLClass",
+ "_id": "AAAAAAGA2r8tmC9NiKo=",
+ "_parent": {
+ "$ref": "AAAAAAFF+qBWK6M3Z8Y="
+ },
+ "name": "TankSoundControler",
+ "ownedElements": [
+ {
+ "_type": "UMLGeneralization",
+ "_id": "AAAAAAGA2r8xtC+FTUY=",
+ "_parent": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "source": {
+ "$ref": "AAAAAAGA2r8tmC9NiKo="
+ },
+ "target": {
+ "$ref": "AAAAAAGAt2yrkNd9c4Q="
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/TankMan/doc/TankMan.png b/TankMan/doc/TankMan.png
new file mode 100644
index 0000000..9a35c97
Binary files /dev/null and b/TankMan/doc/TankMan.png differ
diff --git a/TankMan/doc/classes.dot b/TankMan/doc/classes.dot
new file mode 100644
index 0000000..8ca4ee7
--- /dev/null
+++ b/TankMan/doc/classes.dot
@@ -0,0 +1,32 @@
+digraph "classes" {
+rankdir=BT
+charset="utf-8"
+"MLGame.games.Game.src.BattleMode.BattleMode" [color="black", fontcolor="black", label="{MLGame.games.Game.src.BattleMode.BattleMode|bullet_stations\lbullets\lis_debug : bool\lis_invincible : bool\lis_through_wall : bool\loil_stations\lplayer_1P\lplayer_2P\lplayers\lstate\lstatus\lwalls\l|check_collisions()\lcheck_events()\lcreate_bullet(shoot_info)\lcreate_game_data_to_player()\lcreate_init_image_data()\lcreate_scene_info()\ldraw_sprite_data()\ldraw_text_data()\lget_result(): list\lreset()\lupdate(command: dict)\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.Bullet.Bullet" [color="black", fontcolor="black", label="{MLGame.games.Game.src.Bullet.Bullet|angle\lhit_rect\lmove : dict\lrect\lrot : int\lspeed : int\l|get_image_data()\lupdate()\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.SingleMode.SingleMode" [color="black", fontcolor="black", label="{MLGame.games.Game.src.SingleMode.SingleMode|all_sprites\lframe_limit\lis_paused : bool\lis_sound : bool\lmap\lmap_path : str\lstate\lstatus\lused_frame : int\lwindow\l|check_collisions()\lcheck_events()\lcreate_game_data_to_player()\lcreate_init_image_data()\lcreate_scene_info()\ldraw_sprite_data()\ldraw_text_data()\lget_result(): list\lplay_music(music_path: str, volume: float)\lplay_sound(music_path: str, volume: float, max_time: int)\lreset()\lupdate(command: dict)\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.I_Commander.I_Commander" [color="black", fontcolor="black", label="{MLGame.games.Game.src.I_Commander.I_Commander|\l|getControlDict(): dict\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.I_Commander.KeyBoardCommander" [color="black", fontcolor="black", label="{MLGame.games.Game.src.I_Commander.KeyBoardCommander|brakeKey\lmoveLeftKey\lmoveRightKey\lno : int\lspeedKey\l|getControlDict()\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.Wall.Wall" [color="black", fontcolor="black", label="{MLGame.games.Game.src.Wall.Wall|img_path_list : list\llives : int\l|collide_with_bullets()\lget_image_data()\lget_image_init_data()\lget_info()\lget_xy_pos()\lupdate(): None\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.PacPlayer.PacPlayer" [color="black", fontcolor="black", label="{MLGame.games.Game.src.PacPlayer.PacPlayer|hit_rect\limage_data : dict\limage_init_data : dict\lis_alive : bool\llives : int\lorigin_center\lorigin_size : tuple\lrect\lresult_info : dict\lscore : int\lused_frame : int\lvel\l|act(commands: str)\lcollide_with_walls()\lget_image_data()\lget_image_init_data()\lget_info()\lget_origin_size()\lget_xy_pos()\lreset()\lupdate(commands: str)\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.Props.Props" [color="black", fontcolor="black", label="{MLGame.games.Game.src.Props.Props|hit_rect\limage_data : dict\limage_init_data : dict, list\limg_path_list : list\lrect\l|get_image_data()\lget_image_init_data()\lget_info()\lget_size()\lget_xy_pos()\lupdate()\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.Station.Station" [color="black", fontcolor="black", label="{MLGame.games.Game.src.Station.Station|capacity : int\lcool_down\lcount_frame : int\lpower : int\lstation_name : str\l|get_image_data()\lget_image_init_data()\lget_info()\lget_power()\lupdate()\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.Game.Game" [color="black", fontcolor="black", label="{MLGame.games.Game.src.Game.Game|attachements : list\lframe_count : int\lgame_mode\lgame_result_state\lget_scene_info\lis_sound : bool\lmap_path\lscene\ltime_limit : int\l|ai_clients()\lgame_to_player_data(): dict\lget_game_result()\lget_keyboard_command()\lget_scene_init_data(): dict\lget_scene_progress_data(): dict\lis_running()\lrank()\lreset()\lset_game_mode()\lupdate(commands: dict)\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.TiledMap.TiledMap" [color="black", fontcolor="black", label="{MLGame.games.Game.src.TiledMap.TiledMap|height : int\ltmx_data\lwidth : int\l|create_img_init_data(img_no_list: list)\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.Player.Player" [color="black", fontcolor="black", label="{MLGame.games.Game.src.Player.Player|angle : float, int\limage_id\limg_path\lis_alive : bool\lis_backward : bool\lis_forward : bool\lis_shoot : bool\llast_shoot_frame : int\llives\lmove : dict\loil : float, int\lpower : int\lrect\lrot : int\lrot_speed : int\lscore : int\lshield : int\lspeed : int\lsurface\lused_frame : int\l|act(commands: str)\lbackward()\lcollide_with_bullets()\lcollide_with_walls()\lcreate_shoot_info()\lforward()\lget_image_data()\lget_image_init_data()\lget_info()\lrotate()\lturn_left()\lturn_right()\lupdate(commands: str)\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.sound_controller.SoundController" [color="black", fontcolor="black", label="{MLGame.games.Game.src.sound_controller.SoundController|is_sound_on : bool\lwarn_sound\l|play_normal_music()\l}", shape="record", style="solid"];
+"abc.ABC" [color="black", fontcolor="black", label="{abc.ABC|\l|}", shape="record", style="solid"];
+"collections.defaultdict" [color="black", fontcolor="black", label="{collections.defaultdict|default_factory : NoneType\l|}", shape="record", style="solid"];
+"pytmx.pytmx.TiledElement" [color="black", fontcolor="black", label="{pytmx.pytmx.TiledElement|properties : dict\l|from_xml_string(xml_string: str): TiledElement\l}", shape="record", style="solid"];
+"pytmx.pytmx.TiledMap" [color="black", fontcolor="black", label="{pytmx.pytmx.TiledMap|background_color : NoneType\lfilename : Optional[str]\lgidmap\lheight : int\lhexsidelength : int\limage_loader\limagemap : dict\limages : list\linvert_y\llayernames : dict\llayers : list\lload_all_tiles\lmaxgid : int\lnextobjectid : int\lobjectgroups\lobjects\lobjects_by_id : dict\lobjects_by_name : dict\loptional_gids\lorientation : str\lrenderorder : str\lstaggeraxis : NoneType\lstaggerindex : NoneType\ltile_properties : dict\ltiledgidmap : dict\ltiledversion : str\ltileheight : int\ltilesets : list\ltilewidth : int\lversion : str\lvisible_layers\lvisible_object_groups\lvisible_tile_layers\lwidth : int\l|add_layer(layer: Union[TiledTileLayer, TiledImageLayer, TiledGroupLayer, TiledObjectGroup]): None\ladd_tileset(tileset: TiledTileset): None\lget_layer_by_name(name: str)\lget_object_by_id(obj_id: int): TiledObject\lget_object_by_name(name): TiledObject\lget_tile_colliders(): Iterable[Tuple[int, List[Dict]]]\lget_tile_gid(x: int, y: int, layer: int): int\lget_tile_image(x: int, y: int, layer: int)\lget_tile_image_by_gid(gid: int)\lget_tile_locations_by_gid(gid: int): Iterable[MapPoint]\lget_tile_properties(x: int, y: int, layer: int): Optional[Dict]\lget_tile_properties_by_gid(gid: int): Optional[Dict]\lget_tile_properties_by_layer(layer: int)\lget_tileset_from_gid(gid: int): TiledTileset\lmap_gid(tiled_gid: int): Optional[List[int]]\lmap_gid2(tiled_gid: int): List[Tuple[int, Optional[int]]]\lparse_xml(node: ElementTree.Element)\lregister_gid(tiled_gid: int, flags: Optional[TileFlags]): int\lreload_images(): None\lset_tile_properties(gid: int, properties: dict): None\l}", shape="record", style="solid"];
+"MLGame.games.Game.src.BattleMode.BattleMode" -> "MLGame.games.Game.src.SingleMode.SingleMode" [arrowhead="empty", arrowtail="none"];
+"MLGame.games.Game.src.I_Commander.I_Commander" -> "abc.ABC" [arrowhead="empty", arrowtail="none"];
+"MLGame.games.Game.src.I_Commander.KeyBoardCommander" -> "MLGame.games.Game.src.I_Commander.I_Commander" [arrowhead="empty", arrowtail="none"];
+"MLGame.games.Game.src.Wall.Wall" -> "MLGame.games.Game.src.Props.Props" [arrowhead="empty", arrowtail="none"];
+"MLGame.games.Game.src.Player.Player" -> "MLGame.games.Game.src.PacPlayer.PacPlayer" [arrowhead="empty", arrowtail="none"];
+"pytmx.pytmx.TiledMap" -> "pytmx.pytmx.TiledElement" [arrowhead="empty", arrowtail="none"];
+"MLGame.games.Game.src.BattleMode.BattleMode" -> "MLGame.games.Game.src.Game.Game" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="game_mode", style="solid"];
+"MLGame.games.Game.src.BattleMode.BattleMode" -> "MLGame.games.Game.src.Game.Game" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="game_mode", style="solid"];
+"MLGame.games.Game.src.TiledMap.TiledMap" -> "MLGame.games.Game.src.SingleMode.SingleMode" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="map", style="solid"];
+"collections.defaultdict" -> "pytmx.pytmx.TiledMap" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="gidmap", style="solid"];
+"pytmx.pytmx.TiledMap" -> "MLGame.games.Game.src.TiledMap.TiledMap" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="tmx_data", style="solid"];
+}
diff --git a/TankMan/doc/packages.dot b/TankMan/doc/packages.dot
new file mode 100644
index 0000000..baf45af
--- /dev/null
+++ b/TankMan/doc/packages.dot
@@ -0,0 +1,36 @@
+digraph "packages" {
+rankdir=BT
+charset="utf-8"
+"MLGame.games.Game.src" [color="black", label="MLGame.games.Game.src", shape="box", style="solid"];
+"MLGame.games.Game.src.BattleMode" [color="black", label="MLGame.games.Game.src.BattleMode", shape="box", style="solid"];
+"MLGame.games.Game.src.Bullet" [color="black", label="MLGame.games.Game.src.Bullet", shape="box", style="solid"];
+"MLGame.games.Game.src.SingleMode" [color="black", label="MLGame.games.Game.src.SingleMode", shape="box", style="solid"];
+"MLGame.games.Game.src.I_Commander" [color="black", label="MLGame.games.Game.src.I_Commander", shape="box", style="solid"];
+"MLGame.games.Game.src.Wall" [color="black", label="MLGame.games.Game.src.Wall", shape="box", style="solid"];
+"MLGame.games.Game.src.PacPlayer" [color="black", label="MLGame.games.Game.src.PacPlayer", shape="box", style="solid"];
+"MLGame.games.Game.src.Props" [color="black", label="MLGame.games.Game.src.Props", shape="box", style="solid"];
+"MLGame.games.Game.src.Station" [color="black", label="MLGame.games.Game.src.Station", shape="box", style="solid"];
+"MLGame.games.Game.src.Game" [color="black", label="MLGame.games.Game.src.Game", shape="box", style="solid"];
+"MLGame.games.Game.src.TiledMap" [color="black", label="MLGame.games.Game.src.TiledMap", shape="box", style="solid"];
+"MLGame.games.Game.src.Player" [color="black", label="MLGame.games.Game.src.Player", shape="box", style="solid"];
+"MLGame.games.Game.src.collide_hit_rect" [color="black", label="MLGame.games.Game.src.collide_hit_rect", shape="box", style="solid"];
+"MLGame.games.Game.src.env" [color="black", label="MLGame.games.Game.src.env", shape="box", style="solid"];
+"MLGame.games.Game.src.sound_controller" [color="black", label="MLGame.games.Game.src.sound_controller", shape="box", style="solid"];
+"MLGame.games.Game.src.test_case" [color="black", label="MLGame.games.Game.src.test_case", shape="box", style="solid"];
+"MLGame.games.Game.src.BattleMode" -> "MLGame.games.Game.src.Bullet" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.BattleMode" -> "MLGame.games.Game.src.SingleMode" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.BattleMode" -> "MLGame.games.Game.src.collide_hit_rect" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Bullet" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.SingleMode" -> "MLGame.games.Game.src.TiledMap" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.SingleMode" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Wall" -> "MLGame.games.Game.src.Props" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Wall" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Station" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Game" -> "MLGame.games.Game.src.BattleMode" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Game" -> "MLGame.games.Game.src.sound_controller" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Player" -> "MLGame.games.Game.src.PacPlayer" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.Player" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.collide_hit_rect" -> "MLGame.games.Game.src.Station" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.collide_hit_rect" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+"MLGame.games.Game.src.sound_controller" -> "MLGame.games.Game.src.env" [arrowhead="open", arrowtail="none"];
+}
diff --git a/TankMan/game_config.json b/TankMan/game_config.json
new file mode 100644
index 0000000..00b06dc
--- /dev/null
+++ b/TankMan/game_config.json
@@ -0,0 +1,77 @@
+{
+ "game_name": "TankMan",
+ "version": "funai",
+ "url": "https://github.com/PAIA-Playful-AI-Arena/TankMan.git",
+ "description": "坦克人(Tank Man),一款經典的雙人對戰遊戲,時間內率先擊殺對手獲勝,否則以分數高者獲勝,除了擊中對手外,破壞遊戲物件,以獲得更高積分。注意: 場上資源恢復皆須時間,先到者得,你需要這些補充品以提供前進和射擊的燃油和子彈。",
+ "logo": [
+ "./asset/image/TankMan.svg",
+ "https://raw.githubusercontent.com/PAIA-Playful-AI-Arena/TankMan/main/asset/image/TankMan.svg"
+ ],
+ "user_num": {
+ "min": 2,
+ "max": 6
+ },
+ "game_params": [
+ {
+ "name": "green_team_num",
+ "verbose": "A隊人數為:",
+ "type": "int",
+ "max": 5,
+ "min": 1,
+ "help": "set the number of green_team users.",
+ "default": 1
+ },
+ {
+ "name": "blue_team_num",
+ "verbose": "B隊人數為:",
+ "type": "int",
+ "max": 5,
+ "min": 1,
+ "help": "set the number of blue_team users.",
+ "default": 1
+ },
+ {
+ "name": "is_manual",
+ "verbose": "使否手動遊玩",
+ "type": "str",
+ "choices": [
+ {
+ "verbose": "是",
+ "value": "1"
+ },
+ {
+ "verbose": "否",
+ "value": ""
+ }
+ ],
+ "help": "'1' can turn on the player rotate cool down .",
+ "default": 0
+ },
+ {
+ "name": "frame_limit",
+ "verbose": "遊戲總幀數(Frame)",
+ "type": "int",
+ "max": 3000,
+ "min": 30,
+ "default": 300,
+ "help": "set the frame number of game frame limit."
+ },
+ {
+ "name": "sound",
+ "verbose": "遊戲音效",
+ "type": "str",
+ "choices": [
+ {
+ "verbose": "開",
+ "value": "on"
+ },
+ {
+ "verbose": "關",
+ "value": "off"
+ }
+ ],
+ "help": "'on' can turn on the sound.",
+ "default": "off"
+ }
+ ]
+}
diff --git a/TankMan/geraterate_game.py b/TankMan/geraterate_game.py
new file mode 100644
index 0000000..3738b83
--- /dev/null
+++ b/TankMan/geraterate_game.py
@@ -0,0 +1,76 @@
+import importlib
+from contest import Contest
+def import_player(group_number, player_number):
+ module_name = f"ml.Group_{group_number}.ml_play_{player_number}"
+ module = importlib.import_module(module_name)
+ return getattr(module, f"MLPlay")
+
+num_groups = 3
+players_per_group = 3
+
+players = {}
+
+
+Group_mapping = {
+ "A" : "1",
+ "B" : "1",
+ "C" : "1",
+ "D" : "1",
+ "E" : "1",
+ "F" : "1",
+ "G" : "1",
+ "H" : "1",
+ "I" : "1",
+ }
+
+
+for group in Group_mapping:
+ for player in range(1, players_per_group + 1):
+ player_key = f"player{group}_{player}"
+ players[player_key] = import_player(Group_mapping[group], player)
+
+record = {}
+for group in Group_mapping:
+ record[f"Group_{group}"] = 0
+
+if __name__ == '__main__':
+ sound = "off"
+ total_game = 5
+ frame = 2500
+ is_manual = False
+
+
+ Games = []
+ for group_i in Group_mapping:
+ for group_j in Group_mapping:
+ if group_i != group_j:
+ game = { "players" : [
+ players[f"player{group_i}_{k+1}"] for k in range(players_per_group)]
+ + [players[f"player{group_j}_{k+1}"] for k in range(players_per_group)],
+ "home": group_i,
+ "away": group_j
+ }
+ Games.append(game)
+
+
+ for game in Games:
+
+ selected_players = [game["players"][0]('1P', {'sound': sound}),
+ game["players"][1]('2P', {'sound': sound}),
+ game["players"][2]('3P', {'sound': sound}),
+ game["players"][3]('4P', {'sound': sound}),
+ game["players"][4]('5P', {'sound': sound}),
+ game["players"][5]('6P', {'sound': sound})]
+
+ contest_instance = Contest(selected_players, total_game, frame, sound, is_manual)
+ result = contest_instance.run()
+
+ if result["green_team_win"] > result["blue_team_win"]:
+ record[f"Group_{game['home']}"] += 1
+ else:
+ record[f"Group_{game['away']}"] += 1
+ print("home:", "Group_"+str(game["home"]), result["green_team_win"], "VS", result["blue_team_win"] ,"away", "Group_"+str(game["away"]))
+ print(record)
+ input("Press Enter to continue...")
+
+
diff --git a/TankMan/ml/Group_1/ml_play_1.py b/TankMan/ml/Group_1/ml_play_1.py
new file mode 100644
index 0000000..a939d6e
--- /dev/null
+++ b/TankMan/ml/Group_1/ml_play_1.py
@@ -0,0 +1,70 @@
+"""
+The template of the main script of the machine learning process
+"""
+import random
+import pygame
+
+from src.env import IS_DEBUG
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param side A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ # print(keyboard)
+
+ if scene_info["status"] != "GAME_ALIVE":
+ # print(scene_info)
+ return "RESET"
+ move_act = random.randrange(5)
+ aim_act = random.randrange(3)
+ shoot_cd = random.randrange(15, 31)
+
+ is_shoot = 0
+ if scene_info["used_frame"] % shoot_cd == 0:
+ is_shoot = random.randrange(2)
+
+ command = []
+ if move_act == 1:
+ command.append("TURN_RIGHT")
+ elif move_act == 2:
+ command.append("TURN_LEFT")
+ elif move_act == 3:
+ command.append("FORWARD")
+ elif move_act == 4:
+ command.append("BACKWARD")
+
+ if aim_act == 1:
+ command.append("AIM_LEFT")
+ elif aim_act == 2:
+ command.append("AIM_RIGHT")
+
+ if is_shoot and not IS_DEBUG:
+ command.append("SHOOT")
+
+ if self.side == "1P":
+ if pygame.K_b in keyboard:
+ command.append("DEBUG")
+
+ if not command:
+ command.append("NONE")
+
+ return command
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
\ No newline at end of file
diff --git a/TankMan/ml/Group_1/ml_play_2.py b/TankMan/ml/Group_1/ml_play_2.py
new file mode 100644
index 0000000..a939d6e
--- /dev/null
+++ b/TankMan/ml/Group_1/ml_play_2.py
@@ -0,0 +1,70 @@
+"""
+The template of the main script of the machine learning process
+"""
+import random
+import pygame
+
+from src.env import IS_DEBUG
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param side A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ # print(keyboard)
+
+ if scene_info["status"] != "GAME_ALIVE":
+ # print(scene_info)
+ return "RESET"
+ move_act = random.randrange(5)
+ aim_act = random.randrange(3)
+ shoot_cd = random.randrange(15, 31)
+
+ is_shoot = 0
+ if scene_info["used_frame"] % shoot_cd == 0:
+ is_shoot = random.randrange(2)
+
+ command = []
+ if move_act == 1:
+ command.append("TURN_RIGHT")
+ elif move_act == 2:
+ command.append("TURN_LEFT")
+ elif move_act == 3:
+ command.append("FORWARD")
+ elif move_act == 4:
+ command.append("BACKWARD")
+
+ if aim_act == 1:
+ command.append("AIM_LEFT")
+ elif aim_act == 2:
+ command.append("AIM_RIGHT")
+
+ if is_shoot and not IS_DEBUG:
+ command.append("SHOOT")
+
+ if self.side == "1P":
+ if pygame.K_b in keyboard:
+ command.append("DEBUG")
+
+ if not command:
+ command.append("NONE")
+
+ return command
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
\ No newline at end of file
diff --git a/TankMan/ml/Group_1/ml_play_3.py b/TankMan/ml/Group_1/ml_play_3.py
new file mode 100644
index 0000000..a939d6e
--- /dev/null
+++ b/TankMan/ml/Group_1/ml_play_3.py
@@ -0,0 +1,70 @@
+"""
+The template of the main script of the machine learning process
+"""
+import random
+import pygame
+
+from src.env import IS_DEBUG
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param side A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ # print(keyboard)
+
+ if scene_info["status"] != "GAME_ALIVE":
+ # print(scene_info)
+ return "RESET"
+ move_act = random.randrange(5)
+ aim_act = random.randrange(3)
+ shoot_cd = random.randrange(15, 31)
+
+ is_shoot = 0
+ if scene_info["used_frame"] % shoot_cd == 0:
+ is_shoot = random.randrange(2)
+
+ command = []
+ if move_act == 1:
+ command.append("TURN_RIGHT")
+ elif move_act == 2:
+ command.append("TURN_LEFT")
+ elif move_act == 3:
+ command.append("FORWARD")
+ elif move_act == 4:
+ command.append("BACKWARD")
+
+ if aim_act == 1:
+ command.append("AIM_LEFT")
+ elif aim_act == 2:
+ command.append("AIM_RIGHT")
+
+ if is_shoot and not IS_DEBUG:
+ command.append("SHOOT")
+
+ if self.side == "1P":
+ if pygame.K_b in keyboard:
+ command.append("DEBUG")
+
+ if not command:
+ command.append("NONE")
+
+ return command
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
\ No newline at end of file
diff --git a/TankMan/ml/QT.py b/TankMan/ml/QT.py
new file mode 100644
index 0000000..c553fd0
--- /dev/null
+++ b/TankMan/ml/QT.py
@@ -0,0 +1,39 @@
+import numpy as np
+import pandas as pd
+
+class QLearningTable:
+ def __init__(self,actions,learning_rate=0.05,reward_decay=0.9,e_greedy=0.1):
+
+ self.actions=actions
+ self.lr=learning_rate
+ self.gamma=reward_decay
+ self.epsilon=e_greedy
+
+ self.q_table=pd.DataFrame(columns=self.actions,dtype=np.float64)
+
+ def choose_action(self,observation):
+ self.check_state_exist(observation)
+
+ #action selection
+ if np.random.uniform()>self.epsilon:
+ state_action =self.q_table.loc[observation,:]
+ action =np.random.choice(state_action[state_action==np.max(state_action)].index)
+ else:
+ action = np.random.choice(self.actions)
+
+ return action
+
+ def learn(self,s,a,r,s_):
+ self.check_state_exist(s)
+ self.check_state_exist(s_)
+ q_predict=self.q_table.loc[s,a]
+ if s_!='Game_over' or s_!='Game_pass':
+ q_target =r+self.gamma*self.q_table.loc[s_,:].max()
+ else:
+ q_target=r
+ self.q_table.loc[s,a]+=self.lr*(q_target-q_predict)
+
+ def check_state_exist(self,state):
+ if state not in list(self.q_table.index):
+ new_row = pd.Series([0]*len(self.actions), index=self.q_table.columns, name=state)
+ self.q_table = pd.concat([self.q_table, pd.DataFrame(new_row).T])
\ No newline at end of file
diff --git a/TankMan/ml/__init__.py b/TankMan/ml/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/TankMan/ml/ml_play.py b/TankMan/ml/ml_play.py
new file mode 100644
index 0000000..a939d6e
--- /dev/null
+++ b/TankMan/ml/ml_play.py
@@ -0,0 +1,70 @@
+"""
+The template of the main script of the machine learning process
+"""
+import random
+import pygame
+
+from src.env import IS_DEBUG
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param side A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ # print(keyboard)
+
+ if scene_info["status"] != "GAME_ALIVE":
+ # print(scene_info)
+ return "RESET"
+ move_act = random.randrange(5)
+ aim_act = random.randrange(3)
+ shoot_cd = random.randrange(15, 31)
+
+ is_shoot = 0
+ if scene_info["used_frame"] % shoot_cd == 0:
+ is_shoot = random.randrange(2)
+
+ command = []
+ if move_act == 1:
+ command.append("TURN_RIGHT")
+ elif move_act == 2:
+ command.append("TURN_LEFT")
+ elif move_act == 3:
+ command.append("FORWARD")
+ elif move_act == 4:
+ command.append("BACKWARD")
+
+ if aim_act == 1:
+ command.append("AIM_LEFT")
+ elif aim_act == 2:
+ command.append("AIM_RIGHT")
+
+ if is_shoot and not IS_DEBUG:
+ command.append("SHOOT")
+
+ if self.side == "1P":
+ if pygame.K_b in keyboard:
+ command.append("DEBUG")
+
+ if not command:
+ command.append("NONE")
+
+ return command
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
\ No newline at end of file
diff --git a/TankMan/ml/ml_play_manual.py b/TankMan/ml/ml_play_manual.py
new file mode 100644
index 0000000..842391e
--- /dev/null
+++ b/TankMan/ml/ml_play_manual.py
@@ -0,0 +1,79 @@
+"""
+The template of the main script of the machine learning process
+"""
+
+import pygame
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param ai_name A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ if scene_info["status"] != "GAME_ALIVE":
+ # print(scene_info)
+ return "RESET"
+ command = []
+ if self.side == "1P":
+ print(scene_info["oil"])
+ if pygame.K_RIGHT in keyboard:
+ command.append("TURN_RIGHT")
+ elif pygame.K_LEFT in keyboard:
+ command.append("TURN_LEFT")
+ elif pygame.K_UP in keyboard:
+ command.append("FORWARD")
+ elif pygame.K_DOWN in keyboard:
+ command.append("BACKWARD")
+
+ if pygame.K_z in keyboard:
+ command.append("AIM_LEFT")
+ elif pygame.K_x in keyboard:
+ command.append("AIM_RIGHT")
+
+ if pygame.K_m in keyboard:
+ command.append("SHOOT")
+ # debug
+ if pygame.K_b in keyboard:
+ command.append("DEBUG")
+ # paused
+ if pygame.K_t in keyboard:
+ command.append("PAUSED")
+ elif self.side == "2P":
+ if pygame.K_d in keyboard:
+ command.append("TURN_RIGHT")
+ elif pygame.K_a in keyboard:
+ command.append("TURN_LEFT")
+ elif pygame.K_w in keyboard:
+ command.append("FORWARD")
+ elif pygame.K_s in keyboard:
+ command.append("BACKWARD")
+
+ if pygame.K_q in keyboard:
+ command.append("AIM_LEFT")
+ elif pygame.K_e in keyboard:
+ command.append("AIM_RIGHT")
+
+ if pygame.K_f in keyboard:
+ command.append("SHOOT")
+
+ if not command:
+ command.append("NONE")
+
+ return command
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
diff --git a/TankMan/requirements.txt b/TankMan/requirements.txt
new file mode 100644
index 0000000..a4782f0
--- /dev/null
+++ b/TankMan/requirements.txt
@@ -0,0 +1,2 @@
+pytmx == 3.31
+mlgame
\ No newline at end of file
diff --git a/TankMan/scripts/profile.pstats b/TankMan/scripts/profile.pstats
new file mode 100644
index 0000000..e3f25a0
Binary files /dev/null and b/TankMan/scripts/profile.pstats differ
diff --git a/TankMan/scripts/profile.pstats.png b/TankMan/scripts/profile.pstats.png
new file mode 100644
index 0000000..299bd4b
Binary files /dev/null and b/TankMan/scripts/profile.pstats.png differ
diff --git a/TankMan/scripts/profile.sh b/TankMan/scripts/profile.sh
new file mode 100644
index 0000000..9b6bc5a
--- /dev/null
+++ b/TankMan/scripts/profile.sh
@@ -0,0 +1,19 @@
+export FILE=profile.pstats
+
+python -m cProfile -s cumtime -o $FILE \
+-m mlgame \
+-i ../ml/ml_play_template_1P.py \
+-i ../ml/ml_play_template_1P.py \
+-i ../ml/ml_play_template_1P.py \
+-i ../ml/ml_play_template_1P.py \
+-i ../ml/ml_play_template_1P.py \
+-i ../ml/ml_play_template_2P.py \
+-f 120 -1 \
+../ \
+--frame_limit=30 --sound="off"
+
+python -m gprof2dot -f pstats $FILE | dot -T png -o ${FILE}.png
+
+# python -m pstats profile.pstats
+# sort cumtime
+# stats
diff --git a/TankMan/scripts/test_local_server.sh b/TankMan/scripts/test_local_server.sh
new file mode 100644
index 0000000..afd9f6b
--- /dev/null
+++ b/TankMan/scripts/test_local_server.sh
@@ -0,0 +1,7 @@
+python -m mlgame \
+--ws="wss://demo.piesocket.com/v3/jesse?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV" \
+-f 30 --nd -1 \
+-i ../ml/ml_play_template_1P.py \
+-i ../ml/ml_play_template_2P.py \
+../ \
+--frame_limit 300
diff --git a/TankMan/src/Bullet.py b/TankMan/src/Bullet.py
new file mode 100644
index 0000000..ff051dd
--- /dev/null
+++ b/TankMan/src/Bullet.py
@@ -0,0 +1,80 @@
+from os import path
+
+import pygame
+from mlgame.view.view_model import create_asset_init_data, create_image_view_data
+
+from .env import WINDOW_HEIGHT, WINDOW_WIDTH, IMAGE_DIR
+
+Vec = pygame.math.Vector2
+
+
+class Bullet(pygame.sprite.Sprite):
+ def __init__(self, construction, **kwargs):
+ super().__init__()
+ self.id = construction["_id"]
+ self.no = construction["_no"]
+ self.rect = pygame.Rect((0, 0), construction["_init_size"])
+ self.rect.center = construction["_init_pos"]
+ self.rot = kwargs["rot"]
+ self.play_rect_area = kwargs["play_rect_area"]
+ self.speed = kwargs["bullet_speed"]
+ self.map_width = WINDOW_WIDTH
+ self.map_height = WINDOW_HEIGHT
+ self.angle = 3.14 / 180 * (self.rot + 90)
+ # Refactor
+ if 7 > self.angle > 6:
+ self.angle = 0
+ self.sqrt2 = 1.414
+ self.move = {"left_up": Vec(-self.speed/self.sqrt2, -self.speed/self.sqrt2),
+ "right_up": Vec(self.speed/self.sqrt2, -self.speed/self.sqrt2),
+ "left_down": Vec(-self.speed/self.sqrt2, self.speed/self.sqrt2),
+ "right_down": Vec(self.speed/self.sqrt2, self.speed)/self.sqrt2,
+ "left": Vec(-self.speed, 0), "right": Vec(self.speed, 0), "up": Vec(0, -self.speed),
+ "down": Vec(0, self.speed)}
+
+ self.max_travel_distance = (kwargs["bullet_travel_distance"] // self.speed + 1) * self.speed
+
+ self.travel_distance = 0
+
+ def update(self):
+ self.travel_distance += self.speed
+
+ if self.play_rect_area.top < self.rect.centery < self.play_rect_area.bottom \
+ and self.play_rect_area.left < self.rect.centerx < self.play_rect_area.right:
+ is_out = False
+ else:
+ is_out = True
+
+ if is_out or self.travel_distance >= self.max_travel_distance:
+ self.kill()
+
+ if self.rot == 0 or self.rot == 360:
+ self.rect.center += self.move["left"]
+ elif self.rot == 315 or self.rot == -45:
+ self.rect.center += self.move["left_up"]
+ elif self.rot == 270 or self.rot == -90:
+ self.rect.center += self.move["up"]
+ elif self.rot == 225 or self.rot == -135:
+ self.rect.center += self.move["right_up"]
+ elif self.rot == 180 or self.rot == -180:
+ self.rect.center += self.move["right"]
+ elif self.rot == 135 or self.rot == -225:
+ self.rect.center += self.move["right_down"]
+ elif self.rot == 90 or self.rot == -270:
+ self.rect.center += self.move["down"]
+ elif self.rot == 45 or self.rot == -315:
+ self.rect.center += self.move["left_down"]
+
+ def get_obj_progress_data(self):
+ img_id = "team_a_bullet" if self.id == 1 else "team_b_bullet"
+ return create_image_view_data(img_id, self.rect.x, self.rect.y, self.rect.width, self.rect.height,
+ self.angle)
+
+ def get_data_from_obj_to_game(self) -> dict:
+ info = {"id": f"{self.no}P_bullet",
+ "x": self.rect.x,
+ "y": self.rect.y,
+ "speed": self.speed,
+ "rot": self.rot
+ }
+ return info
diff --git a/TankMan/src/Game.py b/TankMan/src/Game.py
new file mode 100644
index 0000000..64282a8
--- /dev/null
+++ b/TankMan/src/Game.py
@@ -0,0 +1,116 @@
+from os import path
+
+import pygame
+from mlgame.game.paia_game import PaiaGame, GameStatus
+from mlgame.view.view_model import Scene
+
+from .TeamBattleMode import TeamBattleMode
+from .game_module.fuctions import get_sprites_progress_data
+
+MAP_WIDTH = 1000
+MAP_HEIGHT = 600
+GAME_DIR = path.dirname(__file__)
+MAP_DIR = path.join(GAME_DIR, "..", "asset", "maps")
+SOUND_DIR = path.join(GAME_DIR, "..", "asset", "sound")
+IMAGE_DIR = path.join(GAME_DIR, "..", "asset", "image")
+
+
+class Game(PaiaGame):
+ def __init__(self, user_num: int, green_team_num: int, blue_team_num: int, is_manual: str, frame_limit: int, sound: str):
+ super().__init__(user_num)
+ # init game
+ self.green_team_num = green_team_num
+ self.blue_team_num = blue_team_num
+ self.is_paused = False
+ self.is_debug = False
+ self.is_sound = False
+ self.is_manual = False
+ if sound == "on":
+ self.is_sound = True
+ if is_manual:
+ self.is_manual = True
+ self.attachements = []
+ self.frame_limit = frame_limit
+ self.game_mode = self.set_game_mode()
+ self.scene = Scene(width=self.game_mode.scene_width, height=self.game_mode.scene_height, color="#ffffff",
+ bias_y=50)
+
+ def get_data_from_game_to_player(self) -> dict:
+ to_players_data = self.game_mode.get_ai_data_to_player()
+ return to_players_data
+
+ def update(self, commands: dict):
+ self.handle_event(commands)
+ self.game_mode.debugging(self.is_debug)
+ if not self.is_paused:
+ self.frame_count += 1
+ self.game_mode.update(commands)
+ if not self.is_running():
+ return "RESET"
+
+ def reset(self):
+ self.frame_count = 0
+ self.game_mode.reset()
+ # self.rank()
+
+ def get_scene_init_data(self) -> dict:
+ """
+ Get the scene and object information for drawing on the web
+ """
+ game_info = {'scene': self.scene.__dict__,
+ 'assets': self.game_mode.get_init_image_data()}
+
+ return game_info
+
+ def get_scene_progress_data(self) -> dict:
+ """
+ Get the position of src objects for drawing on the web
+ """
+ scene_progress = {'background': [],
+ 'object_list': [*self.game_mode.background, *self.get_obj_progress_data()],
+ 'toggle_with_bias': [*self.game_mode.get_toggle_with_bias_data()],
+ 'toggle': self.game_mode.get_toggle_progress_data(),
+ 'foreground': [],
+ 'user_info': [],
+ 'game_sys_info': {}}
+
+ return scene_progress
+
+ def get_obj_progress_data(self):
+ obj_list = []
+ for sprites in self.game_mode.obj_list:
+ obj_list.extend(get_sprites_progress_data(sprites))
+ obj_list.extend(self.game_mode.obj_rect_list)
+ return obj_list
+
+ def get_game_result(self):
+ """
+ Get the src result for the web
+ """
+ self.rank()
+ return {"frame_used": self.frame_count,
+ "state": self.game_result_state,
+ "attachment": self.attachements
+ }
+
+ def is_running(self):
+ return self.game_mode.status == GameStatus.GAME_ALIVE
+
+ def rank(self):
+ self.game_result_state = self.game_mode.state
+ self.attachements = self.game_mode.get_player_result()
+ return self.attachements
+
+ def handle_event(self, commands):
+ if ["DEBUG"] in commands.values():
+ self.is_debug = not self.is_debug
+ if ["PAUSED"] in commands.values():
+ self.is_paused = not self.is_paused
+
+ def set_game_mode(self):
+ sound_path = ""
+ if self.is_sound:
+ sound_path = SOUND_DIR
+ play_rect_area = pygame.Rect(0, 0, MAP_WIDTH, MAP_HEIGHT)
+ game_mode = TeamBattleMode(self.green_team_num, self.blue_team_num, self.is_manual, self.frame_limit, sound_path, play_rect_area)
+ return game_mode
diff --git a/TankMan/src/GenerateMap.py b/TankMan/src/GenerateMap.py
new file mode 100644
index 0000000..8630581
--- /dev/null
+++ b/TankMan/src/GenerateMap.py
@@ -0,0 +1,153 @@
+from os import path
+import random
+import math
+
+
+PLAYER_NUM = 1
+OIL_NUM = 2
+BULLET_NUM = 2
+MAP_DIR = path.join(path.dirname(__file__), "..", "asset", 'maps')
+MAP_VERSION = "1.9"
+TILED_VERSION = "1.9.2"
+
+
+class MapGenerator:
+
+ def __init__(self, green_team_num : int, blue_team_num : int, width : int, height : int) -> None:
+
+ self.green_team_num = green_team_num
+ self.blue_team_num = blue_team_num
+
+ # height should be a factor of 600
+ # default is 20, 12
+
+ self.width = width
+ self.height = height
+
+ self.height_per_tile = math.floor(600/self.height)
+ self.width_per_tile = self.height_per_tile
+
+ self.screen_width = self.width * self.height_per_tile
+ self.screen_height = 700
+
+ def getTileSize(self) -> int:
+ return self.height_per_tile
+
+ def getScreeenSize(self) -> tuple:
+ return self.screen_width, self.screen_height
+
+ def pos2index(self, x : int, y : int) -> int:
+ return y * (self.width * 2 + 1) + x * 2
+
+ def mirrored_pos(self, x : int, y : int) -> tuple:
+ return self.width - x - 1, self.height - y - 1
+
+ def random_pos(self, map_arr) -> tuple:
+ random_x, random_y = random.randint(1, self.width-2), random.randint(1, self.height-2)
+ mir_x, mir_y = self.mirrored_pos(random_x, random_y)
+ while map_arr[random_y][random_x] != 0 or map_arr[mir_y][mir_x] != 0:
+ random_x, random_y = random.randint(1, self.width-2), random.randint(1, self.height-2)
+ mir_x, mir_y = self.mirrored_pos(random_x, random_y)
+
+ return random_x, random_y
+
+ def generate_map_str(self) -> str:
+
+ # generate default map[y][x]
+ map_arr = [[0 for _ in range(self.width)] for _ in range(self.height)]
+
+ for x in range(self.width):
+ map_arr[0][x] = 3
+ map_arr[self.height-1][x] = 3
+
+ for y in range(self.height):
+ map_arr[y][0] = 3
+ map_arr[y][self.width-1] = 3
+
+ if self.width % 2 == 1:
+ for y in range(1, self.height-1):
+ map_arr[y][self.width//2] = 3
+ else:
+ for y in range(1, self.height):
+ if y < math.ceil(self.height/2):
+ map_arr[y][self.width//2-1] = 3
+ else:
+ map_arr[y][self.width//2] = 3
+
+ # add player
+ for _ in range(min(self.green_team_num, self.blue_team_num)):
+ rand_x, rand_y = self.random_pos(map_arr)
+ while rand_x < self.width // 2:
+ rand_x, rand_y = self.random_pos(map_arr)
+ mir_x, mir_y = self.mirrored_pos(rand_x, rand_y)
+
+ map_arr[rand_y][rand_x] = 1
+ map_arr[mir_y][mir_x] = 2
+
+ # add remaining green team
+ for _ in range(self.green_team_num - self.blue_team_num):
+ rand_x, rand_y = self.random_pos(map_arr)
+ while rand_x < self.width // 2:
+ rand_x, rand_y = self.random_pos(map_arr)
+
+ map_arr[rand_y][rand_x] = 1
+
+ # add remaining blue team
+ for _ in range(self.blue_team_num - self.green_team_num):
+ rand_x, rand_y = self.random_pos(map_arr)
+ while rand_x >= self.width // 2:
+ rand_x, rand_y = self.random_pos(map_arr)
+
+ map_arr[rand_y][rand_x] = 2
+
+ # add bullet station
+ for _ in range(BULLET_NUM):
+ rand_x, rand_y = self.random_pos(map_arr)
+ mir_x, mir_y = self.mirrored_pos(rand_x, rand_y)
+ map_arr[rand_y][rand_x] = 4
+ map_arr[mir_y][mir_x] = 4
+
+ # add oil station
+ for _ in range(OIL_NUM):
+ rand_x, rand_y = self.random_pos(map_arr)
+ mir_x, mir_y = self.mirrored_pos(rand_x, rand_y)
+ map_arr[rand_y][rand_x] = 5
+ map_arr[mir_y][mir_x] = 5
+
+ map_str = ""
+
+ for row in map_arr:
+ for id in row:
+ map_str += str(id) + ","
+ map_str += "\n"
+ map_str = map_str[:-2]
+
+ return map_str
+
+ def generate_map(self):
+ map_name = f"map_{self.green_team_num}_v_{self.blue_team_num}.tmx"
+ map_path = path.join(MAP_DIR, map_name)
+ print(f'generate map at : {map_path}', flush=True)
+
+ with open(map_path, "w") as file:
+ # file.write("test")
+ file.write(f"""\
+
+
+ """)
+
+
+if __name__ == "__main__":
+ map_generator = MapGenerator(1, 1)
+ map_generator.generate_map()
+
+
diff --git a/TankMan/src/Gun.py b/TankMan/src/Gun.py
new file mode 100644
index 0000000..97f87b7
--- /dev/null
+++ b/TankMan/src/Gun.py
@@ -0,0 +1,106 @@
+from os import path
+
+import pygame
+from mlgame.view.view_model import (create_asset_init_data,
+ create_image_view_data)
+
+from .env import IMAGE_DIR
+
+
+class Gun(pygame.sprite.Sprite):
+ def __init__(self, id, pos, size, **kwargs):
+ super().__init__()
+ self.id = id
+ self.rect = pygame.Rect(pos, size)
+ self.origin_size = (self.rect.width, self.rect.height)
+ self.draw_pos = self.rect.topleft
+ self.surface = pygame.Surface(self.origin_size)
+ self.rot = 0
+ self.rot_speed = 45
+
+ self.used_frame = 0
+ self.last_turn_frame = self.used_frame
+ self.act_cd = kwargs["act_cd"]
+
+ self.is_alive = True
+ self.is_turn_left = False
+ self.is_turn_right = False
+
+ if self.id == 1:
+ self.pivot_offset = pygame.Vector2(-8, 0)
+ else:
+ self.pivot_offset = pygame.Vector2(8, 0)
+
+ def update(self, gun_pos):
+ self.used_frame += 1
+ self.rotate()
+
+ if not self.act_cd:
+ self.is_turn_right = False
+ self.is_turn_left = False
+ elif self.used_frame - self.last_turn_frame > self.act_cd:
+ self.is_turn_right = False
+ self.is_turn_left = False
+
+ self.rect.center = gun_pos + self.pivot_offset.rotate(-self.rot)
+ self.draw_pos = self.rect.topleft
+
+ def rotate(self):
+ self.rot = self.rot % 360
+ self.angle = 3.14 / 180 * self.rot
+ new_sur = pygame.transform.rotate(self.surface, self.rot)
+ origin_center = self.rect.center
+ self.rect = new_sur.get_rect()
+ self.rect.center = origin_center
+ self.draw_pos = self.rect.topleft
+
+ def turn_left(self):
+ if self.is_turn_left:
+ return
+
+ self.rot += self.rot_speed
+ self.last_turn_frame = self.used_frame
+
+ self.is_turn_left = True
+ self.is_turn_right = False
+
+ def turn_right(self):
+ if self.is_turn_right:
+ return
+
+ self.rot -= self.rot_speed
+ self.last_turn_frame = self.used_frame
+
+ self.is_turn_left = False
+ self.is_turn_right = True
+
+ def get_rot(self):
+ if self.id == 2:
+ return (self.rot + 180) % 360
+ return self.rot
+
+ def get_obj_progress_data(self) -> dict:
+ if not self.is_alive:
+ return {}
+ image_data = create_image_view_data(
+ f"{self.id}P_gun", *self.draw_pos, *self.origin_size, self.angle
+ )
+ return image_data
+
+ def get_obj_init_data(self) -> list:
+ img_data = {
+ "1P_gun": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/1P_gun.svg",
+ "2P_gun": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/2P_gun.svg",
+ }
+ image_init_data = []
+ for id, url in img_data.items():
+ image_init_data.append(
+ create_asset_init_data(
+ id,
+ self.origin_size[0],
+ self.origin_size[1],
+ path.join(IMAGE_DIR, f"{id}.png"),
+ url,
+ )
+ )
+ return image_init_data
diff --git a/TankMan/src/Player.py b/TankMan/src/Player.py
new file mode 100644
index 0000000..b98954c
--- /dev/null
+++ b/TankMan/src/Player.py
@@ -0,0 +1,325 @@
+from os import path
+import random
+import pygame.draw
+from mlgame.utils.enum import get_ai_name
+from mlgame.view.view_model import create_asset_init_data, create_image_view_data, create_rect_view_data
+from .env import TURN_LEFT_CMD, TURN_RIGHT_CMD, FORWARD_CMD, BACKWARD_CMD, \
+ AIM_LEFT_CMD, AIM_RIGHT_CMD, SHOOT, SHOOT_COOLDOWN, IMAGE_DIR, ORANGE, BLUE, IS_DEBUG
+from .Gun import Gun
+
+Vec = pygame.math.Vector2
+
+
+class Player(pygame.sprite.Sprite):
+ def __init__(self, construction, **kwargs):
+ super().__init__()
+ """
+ 初始化玩家資料
+ construction可直接由TiledMap打包地圖資訊後傳入
+ :param construction:
+ :param kwargs:
+ """
+ self.id = construction["_id"]
+ self.no = construction["_no"]
+ self.rect = pygame.Rect(construction["_init_pos"], construction["_init_size"])
+ self.play_rect_area = kwargs["play_rect_area"]
+ self.origin_xy = self.rect.topleft
+ self.origin_center = self.rect.center
+ self.origin_size = (self.rect.width, self.rect.height)
+ self.original_rect = self.rect.copy()
+ self.draw_pos = self.rect.topleft
+ self.surface = pygame.Surface(self.origin_size)
+ self.angle = 0
+ self.score = 0
+ self.used_frame = 0
+ self.last_shoot_frame = 0
+ self.lives = 3
+ self.power = 10
+ self.vel = Vec(0, 0)
+
+ self.speed = 8
+ self.sqrt2 = 1.414
+ # TODO refactor use vel
+ self.move_dict = {"left_up": Vec(-self.speed/self.sqrt2, -self.speed/self.sqrt2),
+ "right_up": Vec(self.speed/self.sqrt2, -self.speed/self.sqrt2),
+ "left_down": Vec(-self.speed/self.sqrt2, self.speed/self.sqrt2),
+ "right_down": Vec(self.speed/self.sqrt2, self.speed/self.sqrt2),
+ "left": Vec(-self.speed, 0), "right": Vec(self.speed, 0), "up": Vec(0, -self.speed),
+ "down": Vec(0, self.speed)}
+ self.rot = 0
+ self.last_shoot_frame = self.used_frame
+ self.last_turn_frame = self.used_frame
+ self.rot_speed = 45
+ self.oil = 100
+ self.is_alive = True
+ self.is_shoot = False
+ self.is_forward = False
+ self.is_backward = False
+ self.is_turn_right = False
+ self.is_turn_left = False
+ self.collided = False
+ self.action_history = []
+ self.act_cd = kwargs["act_cd"]
+ self.quadrant = 0
+
+ self.gun = Gun(self.id, self.rect.topleft, (self.rect.width, self.rect.height), **kwargs)
+
+ self.calculate_quadrant()
+ self.pre_rect = self.rect
+
+ def calculate_quadrant(self):
+ mid_x = self.play_rect_area.width // 2
+ mid_y = (self.play_rect_area.height - 100) // 2
+ self.quadrant = (
+ 1 if self.rect.x >= mid_x and self.rect.y < mid_y else
+ 2 if self.rect.x < mid_x and self.rect.y < mid_y else
+ 3 if self.rect.x < mid_x and self.rect.y >= mid_y else
+ 4
+ )
+
+ def update(self, command: dict):
+ self.pre_rect = self.rect
+ self.used_frame += 1
+ if self.lives <= 0:
+ self.is_alive = False
+ self.gun.is_alive = False
+ self.lives = 0
+
+ if not self.is_alive:
+ self.rect.topleft = Vec(self.play_rect_area.left + self.origin_size[0]*self.no
+ , self.play_rect_area.top - self.origin_size[1]*2)
+ return
+
+ self.rotate()
+ self.gun.update(self.rect.center)
+
+ if not self.act_cd:
+ self.is_turn_right = False
+ self.is_turn_left = False
+ elif self.used_frame - self.last_turn_frame > self.act_cd:
+ self.is_turn_right = False
+ self.is_turn_left = False
+
+ self.act(command[get_ai_name(self.no - 1)])
+ # check tank if out of playground
+ self.check_if_outofplayground()
+
+ def check_if_outofplayground(self):
+ self.rect.clamp_ip(self.play_rect_area)
+
+ def rotate(self):
+ self.rot = self.rot % 360
+ self.angle = 3.14 / 180 * self.rot
+ new_sur = pygame.transform.rotate(self.surface, self.rot)
+ origin_center = self.rect.center
+ self.rect = new_sur.get_rect()
+ self.rect.center = origin_center
+ self.draw_pos = self.rect.topleft
+
+ def act(self, commands: list):
+ if not commands or self.collided:
+ return None
+
+ # Only one action from the three categories can be executed in one frame
+ move_flag = False
+ shoot_flag = False
+ aim_flag = False
+
+ while commands:
+ command = commands.pop()
+
+ # Shoot
+ if not shoot_flag:
+ if self.power and SHOOT == command:
+ self.shoot()
+
+ shoot_flag = True
+
+ # Aiming
+ # TODO: Maybe the oil should be consumed when aiming
+ if not aim_flag:
+ if command == AIM_LEFT_CMD:
+ self.gun.turn_left()
+ elif command == AIM_RIGHT_CMD:
+ self.gun.turn_right()
+
+ aim_flag = True
+
+ if self.oil <= 0:
+ self.oil = 0
+ self.lives = 0
+ continue
+
+ # Movement
+ if not move_flag:
+ if TURN_LEFT_CMD == command and not self.is_turn_left and TURN_RIGHT_CMD != command:
+ self.oil -= 0.1
+ self.turn_left()
+ self.is_turn_left = True
+ self.is_forward = False
+ self.is_backward = False
+ self.is_turn_right = False
+ self.action_history.append(TURN_LEFT_CMD)
+ elif TURN_RIGHT_CMD == command and not self.is_turn_right and TURN_LEFT_CMD != command:
+ self.oil -= 0.1
+ self.turn_right()
+ self.is_turn_right = True
+ self.is_forward = False
+ self.is_backward = False
+ self.is_turn_left = False
+ self.action_history.append(TURN_RIGHT_CMD)
+ elif FORWARD_CMD == command and BACKWARD_CMD != command:
+ self.oil -= 0.1
+ self.forward()
+ self.is_forward = True
+ self.is_backward = False
+ self.is_turn_right = False
+ self.is_turn_left = False
+ self.action_history.append(FORWARD_CMD)
+ elif BACKWARD_CMD == command and FORWARD_CMD != command:
+ self.oil -= 0.1
+ self.backward()
+ self.is_backward = True
+ self.is_forward = False
+ self.is_turn_right = False
+ self.is_turn_left = False
+ self.action_history.append(BACKWARD_CMD)
+
+ move_flag = True
+
+ self.action_history = self.action_history[-1:]
+
+ def shoot(self):
+ if self.last_shoot_frame == 0 or self.used_frame - self.last_shoot_frame > SHOOT_COOLDOWN:
+ self.last_shoot_frame = self.used_frame
+ self.power -= 1
+ self.is_shoot = True
+
+ def forward(self):
+ if self.id != 1:
+ rot = self.rot + 180
+ if rot >= 360:
+ rot -= 360
+ else:
+ rot = self.rot
+ if rot == 0:
+ self.rect.center += self.move_dict["left"]
+ elif rot == 315:
+ self.rect.center += self.move_dict["left_up"]
+ elif rot == 270:
+ self.rect.center += self.move_dict["up"]
+ elif rot == 225:
+ self.rect.center += self.move_dict["right_up"]
+ elif rot == 180:
+ self.rect.center += self.move_dict["right"]
+ elif rot == 135:
+ self.rect.center += self.move_dict["right_down"]
+ elif rot == 90:
+ self.rect.center += self.move_dict["down"]
+ elif rot == 45:
+ self.rect.center += self.move_dict["left_down"]
+
+ def backward(self):
+ if self.id != 1:
+ rot = self.rot + 180
+ if rot >= 360:
+ rot -= 360
+ else:
+ rot = self.rot
+ if rot == 0:
+ self.rect.center += self.move_dict["right"]
+ elif rot == 315:
+ self.rect.center += self.move_dict["right_down"]
+ elif rot == 270:
+ self.rect.center += self.move_dict["down"]
+ elif rot == 225:
+ self.rect.center += self.move_dict["left_down"]
+ elif rot == 180:
+ self.rect.center += self.move_dict["left"]
+ elif rot == 135:
+ self.rect.center += self.move_dict["left_up"]
+ elif rot == 90:
+ self.rect.center += self.move_dict["up"]
+ elif rot == 45:
+ self.rect.center += self.move_dict["right_up"]
+
+ def turn_left(self):
+ self.last_turn_frame = self.used_frame
+ self.rot += self.rot_speed
+
+ def turn_right(self):
+ self.last_turn_frame = self.used_frame
+ self.rot -= self.rot_speed
+
+ def collide_with_walls(self):
+ self.rect = self.pre_rect
+
+ def collide_with_bullets(self):
+ self.lives -= 1
+
+ def get_power(self, power: int):
+ self.power += power
+ if self.power > 10:
+ self.power = 10
+ elif self.power < 0:
+ self.power = 0
+
+ def get_oil(self, oil: int):
+ self.oil += oil
+ if self.oil > 100:
+ self.oil = 100
+ elif self.oil < 0:
+ self.oil = 0
+
+ def get_rot(self):
+ if self.id == 2:
+ return (self.rot + 180) % 360
+ return self.rot
+
+ def get_data_from_obj_to_game(self) -> dict:
+ info = {"id": f"{self.no}P"
+ , "x": self.rect.x
+ , "y": self.rect.y
+ , "speed": self.speed
+ , "score": self.score
+ , "power": self.power
+ , "oil": self.oil
+ , "lives": self.lives
+ , "angle": self.get_rot()
+ , "gun_angle": self.gun.get_rot()
+ , "cooldown": 0
+ if self.last_shoot_frame == 0
+ or self.used_frame - self.last_shoot_frame > SHOOT_COOLDOWN
+ else SHOOT_COOLDOWN - self.used_frame + self.last_shoot_frame,
+ }
+ return info
+
+ def get_obj_progress_data(self) -> dict:
+ if not self.is_alive:
+ return []
+ image_data = create_image_view_data(f"{self.id}P", *self.draw_pos, *self.origin_size, self.angle)
+ return image_data
+
+ def get_obj_init_data(self) -> list:
+ img_data = {"1P": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/1P_body.svg",
+ "2P": "https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/2P_body.svg"}
+ image_init_data = []
+ for id, url in img_data.items():
+ image_init_data.append(create_asset_init_data(id, self.origin_size[0], self.origin_size[1],
+ path.join(IMAGE_DIR, f"{id}_body.png"), url))
+ return image_init_data
+
+ def get_info_to_game_result(self) -> dict:
+ info = {"no": f"{self.no}P"
+ , "score": self.score
+ , "lives": self.lives
+ }
+ if IS_DEBUG:
+ if self.rect.right > self.play_rect_area.right \
+ or self.rect.left < self.play_rect_area.left \
+ or self.rect.bottom > self.play_rect_area.bottom \
+ or self.rect.top < self.play_rect_area.top:
+ print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!【OUT】!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+ else:
+ print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!【SAFE】!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+ return info
diff --git a/TankMan/src/Station.py b/TankMan/src/Station.py
new file mode 100644
index 0000000..8ac842f
--- /dev/null
+++ b/TankMan/src/Station.py
@@ -0,0 +1,65 @@
+from os import path
+
+import pygame
+from mlgame.view.view_model import create_asset_init_data, create_image_view_data
+
+from .env import IMAGE_DIR, WINDOW_HEIGHT, WINDOW_WIDTH
+
+
+class Station(pygame.sprite.Sprite):
+ def __init__(self, construction, **kwargs):
+ super().__init__()
+ self.id = construction["_id"]
+ self.rect = pygame.Rect(construction["_init_pos"], construction["_init_size"])
+ self.power = kwargs["capacity"]
+ self.angle = 0
+ if self.rect.x >= WINDOW_WIDTH // 2 and self.rect.y < (WINDOW_HEIGHT - 100) // 2:
+ self.quadrant = 1
+ elif self.rect.x < WINDOW_WIDTH // 2 and self.rect.y < (WINDOW_HEIGHT - 100) // 2:
+ self.quadrant = 2
+ elif self.rect.x < WINDOW_WIDTH // 2 and self.rect.y >= (WINDOW_HEIGHT - 100) // 2:
+ self.quadrant = 3
+ else:
+ self.quadrant = 4
+
+ self.spawn_cd = kwargs["spawn_cd"]
+ self.cooldown = 0
+ self.is_alive = True
+
+ def update(self):
+ if not self.is_alive:
+ self.cooldown -= 1
+ if self.cooldown <= 0:
+ self.is_alive = True
+
+ def collect(self):
+ self.is_alive = False
+ self.cooldown = self.spawn_cd
+
+ def get_data_from_obj_to_game(self):
+ if 5 == self.id:
+ info = {"id": "oil", "x": self.rect.x, "y": self.rect.y, "power": self.power if self.is_alive else 0}
+ else:
+ info = {"id": "bullets", "x": self.rect.x, "y": self.rect.y, "power": self.power if self.is_alive else 0}
+ return info
+
+ def get_obj_progress_data(self):
+ if not self.is_alive:
+ return []
+ if 5 == self.id:
+ return create_image_view_data(f"oil", self.rect.x, self.rect.y
+ , self.rect.width, self.rect.height, self.angle)
+ else:
+ return create_image_view_data(f"bullets", self.rect.x, self.rect.y
+ , self.rect.width, self.rect.height, self.angle)
+
+ def get_obj_init_data(self):
+ bullets_id = "bullets"
+ oil_id = "oil"
+ bullets_url = f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{bullets_id}.svg"
+ oil_url = f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{oil_id}.svg"
+ image_init_data = [create_asset_init_data(bullets_id, self.rect.width, self.rect.height,
+ path.join(IMAGE_DIR, f"{bullets_id}.png"), bullets_url)
+ , create_asset_init_data(oil_id, self.rect.width, self.rect.height,
+ path.join(IMAGE_DIR, f"{oil_id}.png"), oil_url)]
+ return image_init_data
diff --git a/TankMan/src/TeamBattleMode.py b/TankMan/src/TeamBattleMode.py
new file mode 100644
index 0000000..99a0270
--- /dev/null
+++ b/TankMan/src/TeamBattleMode.py
@@ -0,0 +1,508 @@
+import random
+import time
+import pygame.event
+import pygame.event
+from src.game_module.SoundController import create_sounds_data, create_bgm_data, SoundController
+from src.game_module.TiledMap import create_construction, TiledMap
+
+from mlgame.game.paia_game import GameResultState, GameStatus
+from mlgame.utils.enum import get_ai_name
+from mlgame.view.view_model import create_asset_init_data, create_text_view_data, \
+ create_rect_view_data, create_line_view_data
+from mlgame.view.view_model import create_image_view_data
+from .Bullet import Bullet
+from .Gun import Gun
+from .Player import Player
+from .Station import Station
+from .Wall import Wall
+from .collide_hit_rect import *
+from .env import *
+from .game_module.fuctions import set_topleft, add_score, set_shoot
+
+
+class TeamBattleMode:
+ def __init__(self, green_team_num: int, blue_team_num: int, is_manual: bool, frame_limit: int, sound_path: str,
+ play_rect_area: pygame.Rect):
+ # init game
+ pygame.init()
+ self.sound_path = sound_path
+ self.green_team_num = green_team_num
+ self.blue_team_num = blue_team_num if (6 - (green_team_num + blue_team_num)) >= 0 else (6 - green_team_num)
+ self.map_name = f"map_{green_team_num}_v_{self.blue_team_num}.tmx" if not IS_DEBUG else f"test_map_{green_team_num}_v_{self.blue_team_num}.tmx"
+ self.map_path = path.join(MAP_DIR, self.map_name)
+ self.map = TiledMap(self.map_path)
+ self.scene_width = self.map.map_width
+ self.scene_height = self.map.map_height + 100
+ self.width_center = self.scene_width // 2
+ self.height_center = self.scene_height // 2
+ self.play_rect_area = play_rect_area
+ self.used_frame = 0
+ self.state = GameResultState.FAIL
+ self.status = GameStatus.GAME_ALIVE
+ self.sound_controller = SoundController(sound_path, self.get_sound_data())
+ self.sound_controller.play_music(self.get_bgm_data())
+ self.frame_limit = frame_limit
+ self.is_manual = is_manual
+ self.obj_rect_list = []
+ self.team_green_score = 0
+ self.team_blue_score = 0
+
+ # control variables
+ self.is_invincible = False
+ self.is_through_wall = False
+ # initialize sprites group
+ self.all_sprites = pygame.sprite.Group()
+ self.players_a = pygame.sprite.Group()
+ self.players_b = pygame.sprite.Group()
+ self.all_players = pygame.sprite.Group()
+ self.guns = pygame.sprite.Group()
+ self.walls = pygame.sprite.Group()
+ self.bullets = pygame.sprite.Group()
+ self.bullet_stations = pygame.sprite.Group()
+ self.oil_stations = pygame.sprite.Group()
+ # init players
+ act_cd = 0
+ if self.is_manual:
+ act_cd = 10
+ # init obj data
+ self.map.add_init_obj_data(PLAYER_1_IMG_NO, Player, act_cd=act_cd, play_rect_area=self.play_rect_area)
+ self.map.add_init_obj_data(PLAYER_2_IMG_NO, Player, act_cd=act_cd, play_rect_area=self.play_rect_area)
+ self.map.add_init_obj_data(WALL_IMG_NO, Wall, margin=8, spacing=8)
+ self.map.add_init_obj_data(BULLET_STATION_IMG_NO, Station, spawn_cd=30, margin=2, spacing=2, capacity=5,
+ quadrant=1)
+ self.map.add_init_obj_data(OIL_STATION_IMG_NO, Station, spawn_cd=30, margin=2, spacing=2, capacity=30,
+ quadrant=1)
+ # create obj
+ all_obj = self.map.create_init_obj_dict()
+ # init players
+ self.players_a.add(all_obj[PLAYER_1_IMG_NO])
+ self.players_b.add(all_obj[PLAYER_2_IMG_NO])
+ no = 1
+ for player in self.players_a:
+ player.no = no
+ no += 1
+ for player in self.players_b:
+ player.no = no
+ no += 1
+ self.all_players.add(*self.players_a, *self.players_b)
+ self.guns.add(*[player.gun for player in self.all_players])
+ self.all_sprites.add(*self.players_a, *self.players_b)
+ self.all_sprites.add(*[player.gun for player in self.all_players])
+ # init walls
+ self.walls.add(all_obj[WALL_IMG_NO])
+ self.all_sprites.add(*self.walls)
+ # init bullet stations
+ self.bullet_stations.add(all_obj[BULLET_STATION_IMG_NO])
+ self.all_sprites.add(*self.bullet_stations)
+ # init oil stations
+ self.oil_stations.add(all_obj[OIL_STATION_IMG_NO])
+ self.all_sprites.add(*self.oil_stations)
+ # init pos list
+ self.all_pos_list = self.map.all_pos_list
+ self.empty_quadrant_pos_dict = self.map.empty_quadrant_pos_dict
+ self.background = []
+ for pos in self.all_pos_list:
+ no = random.randrange(3)
+ self.background.append(
+ create_image_view_data(f"floor_{no}", pos[0], pos[1], 50, 50, 0))
+ self.obj_list = [self.oil_stations, self.bullet_stations, self.bullets, self.all_players, self.guns, self.walls]
+ self.background.append(create_image_view_data("border", 0, -50, self.scene_width, WINDOW_HEIGHT, 0))
+ # init play get new score time
+ self.team_green_maxScoreTime = time.time()
+ self.team_blue_maxScoreTime = time.time()
+ self.team_green_maxScore = 0
+ self.team_blue_maxScore = 0
+ self.change_player_pos()
+
+ def update(self, command: dict):
+ # refactor
+ self.team_green_score = sum([player.score for player in self.players_a if isinstance(player, Player)])
+ self.team_blue_score = sum([player.score for player in self.players_b if isinstance(player, Player)])
+ self.used_frame += 1
+ self.check_collisions()
+ self.walls.update()
+ self.create_bullet(self.all_players)
+ self.bullets.update()
+ self.bullet_stations.update()
+ self.oil_stations.update()
+ self.all_players.update(command)
+ self.get_player_end()
+ if self.used_frame >= self.frame_limit:
+ self.get_game_end()
+
+
+ # check if getting new score
+ if self.team_green_score > self.team_green_maxScore:
+ self.team_green_maxScore = self.team_green_score
+ self.team_green_maxScoreTime = time.time()
+
+ if self.team_blue_score > self.team_blue_maxScore:
+ self.team_blue_maxScore = self.team_blue_score
+ self.team_blue_maxScoreTime = time.time()
+
+ def reset(self):
+ # reset init game
+ self.__init__(self.green_team_num, self.blue_team_num, self.is_manual, self.frame_limit, self.sound_path,
+ self.play_rect_area)
+ # reset player pos
+ self.change_player_pos()
+
+ def get_player_end(self):
+ is_alive_team_green = False
+ is_alive_team_blue = False
+ for player in self.all_players:
+ if isinstance(player, Player) and player.is_alive:
+ if player.no > self.green_team_num and not is_alive_team_blue:
+ is_alive_team_blue = True
+ elif player.no <= self.green_team_num:
+ is_alive_team_green = True
+
+ if not is_alive_team_blue:
+ self.set_result(GameResultState.FINISH, "GREEN_TEAM_WIN")
+ elif not is_alive_team_green:
+ self.set_result(GameResultState.FINISH, "BLUE_TEAM_WIN")
+
+ def get_game_end(self):
+ if self.team_green_score > self.team_blue_score:
+ self.set_result(GameResultState.FINISH, "GREEN_TEAM_WIN")
+ elif self.team_green_score < self.team_blue_score:
+ self.set_result(GameResultState.FINISH, "BLUE_TEAM_WIN")
+ else:
+ # if both the teams have a score of 0
+ if self.team_green_maxScore == 0 and self.team_blue_maxScore == 0:
+ conditions = ["GREEN_TEAM_WIN","BLUE_TEAM_WIN"]
+ chosen_condition = random.choice(conditions)
+ self.set_result(GameResultState.FINISH, chosen_condition)
+ else:
+ # if both teams have scored and their scores are equal
+ if self.team_green_maxScoreTime > self.team_blue_maxScoreTime:
+ self.set_result(GameResultState.FINISH, "BLUE_TEAM_WIN")
+ else:
+ self.set_result(GameResultState.FINISH, "GREEN_TEAM_WIN")
+
+ def set_result(self, state: str, status: str):
+ self.state = state
+ self.status = status
+
+ def get_player_result(self) -> list:
+ """Define the end of game will return the player's info for user"""
+ res = []
+
+ for player in self.all_players:
+ if isinstance(player, Player):
+ if player.no > self.green_team_num:
+ team_id = "blue"
+ else:
+ team_id = "green"
+ get_res = player.get_info_to_game_result()
+
+ get_res["no"] = f"{team_id}_{player.no}P"
+ get_res["player"] = f"{player.no}P"
+ get_res["state"] = self.state
+ get_res["status"] = self.status
+ get_res["used_frame"] = self.used_frame
+ if team_id == "green":
+ timestamp = self.team_green_maxScoreTime
+ else:
+ timestamp = self.team_blue_maxScoreTime
+ get_res["latestScoreTime"] = "{:.3f}".format(timestamp)
+ res.append(get_res)
+
+ for player in res:
+ if player["no"].find("green")!=-1:
+ player["rank"] = 1 if player["status"] == "GREEN_TEAM_WIN" else 2
+ elif player["no"].find("blue")!=-1:
+ player["rank"] = 1 if player["status"] == "BLUE_TEAM_WIN" else 2
+
+ sorted_res = sorted(res, key=lambda x: x["rank"])
+
+ # Result
+ return sorted_res
+
+
+ def check_collisions(self):
+ if not self.is_through_wall:
+ collide_with_walls(self.all_players, self.walls)
+ if not self.is_invincible:
+ player_score_data = collide_with_bullets(self.all_players, self.bullets, self.green_team_num)
+ for player, score in player_score_data.items():
+ self.add_player_score(player, score)
+ # TODO refactor stations
+
+ supply_stations = []
+
+ # Check collision between player and supply stations
+ supply_stations.extend(collide_with_supply_stations(self.all_players, self.bullet_stations))
+ supply_stations.extend(collide_with_supply_stations(self.all_players, self.oil_stations))
+
+ # Check collision between bullet and supply stations
+ supply_stations.extend(collide_with_supply_stations(self.bullets, self.bullet_stations))
+ supply_stations.extend(collide_with_supply_stations(self.bullets, self.oil_stations))
+
+ # Update stations position
+ self.change_obj_pos(supply_stations)
+
+ player_score_data = collide_with_bullets(self.walls, self.bullets)
+ for player, score in player_score_data.items():
+ self.add_player_score(player, score)
+
+ def change_player_pos(self):
+ for player in self.all_players:
+ quadrant = player.quadrant
+ self.empty_quadrant_pos_dict[quadrant].append(player.rect.topleft)
+ if quadrant == 2 or quadrant == 3:
+ player.quadrant = random.choice([2, 3])
+ else:
+ player.quadrant = random.choice([1, 4])
+ quadrant = player.quadrant
+ new_pos = self.empty_quadrant_pos_dict[quadrant].pop(
+ random.randrange(len(self.empty_quadrant_pos_dict[quadrant])))
+ set_topleft(player, new_pos)
+ set_topleft(player.gun, new_pos)
+
+ # TODO move method to Station
+ def change_obj_pos(self, objs=None):
+ if objs is None:
+ return
+ for obj in objs:
+ quadrant = obj.quadrant
+ self.empty_quadrant_pos_dict[quadrant].append(obj.rect.topleft)
+ if quadrant == 2 or quadrant == 3:
+ obj.quadrant = random.choice([2, 3])
+ else:
+ obj.quadrant = random.choice([1, 4])
+ quadrant = obj.quadrant
+ new_pos = self.empty_quadrant_pos_dict[quadrant].pop(
+ random.randrange(len(self.empty_quadrant_pos_dict[quadrant])))
+ set_topleft(obj, new_pos)
+
+ def create_bullet(self, sprites: pygame.sprite.Group):
+ for sprite in sprites:
+ if not sprite.is_shoot:
+ continue
+ self.sound_controller.play_sound("shoot", 0.03, -1)
+ init_data = create_construction(sprite.id, sprite.no, sprite.rect.center, (BULLET_SIZE[0], BULLET_SIZE[1]))
+ bullet = Bullet(init_data, rot=sprite.gun.get_rot(), margin=2, spacing=2, bullet_speed=BULLET_SPEED,
+ bullet_travel_distance=BULLET_TRAVEL_DISTANCE
+ , play_rect_area=self.play_rect_area)
+ self.bullets.add(bullet)
+ self.all_sprites.add(bullet)
+ set_shoot(sprite, False)
+
+ def get_init_image_data(self):
+ init_image_data = []
+ for i in range(3):
+ init_image_data.append(create_asset_init_data(f"floor_{i}", 50, 50
+ , path.join(IMAGE_DIR, f"grass_{i}.png"),
+ f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/grass_{i}.png"))
+ for i in range(15):
+ init_image_data.append(create_asset_init_data(f"hourglass_{i}", 42, 42
+ , path.join(IMAGE_DIR, f"hourglass_{i}.png"),
+ f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/hourglass_{i}.png"))
+ for station in self.bullet_stations:
+ if isinstance(station, Station):
+ for data in station.get_obj_init_data():
+ init_image_data.append(data)
+ break
+ for wall in self.walls:
+ if isinstance(wall, Wall):
+ for data in wall.get_obj_init_data():
+ init_image_data.append(data)
+ break
+ img_id = ["team_a_bullet", "team_b_bullet"]
+ for id in img_id:
+ img_url = f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{id}.svg"
+ bullet_image_init_data = create_asset_init_data(id, BULLET_SIZE[0], BULLET_SIZE[1],
+ path.join(IMAGE_DIR, f"{id}.png"), img_url)
+ init_image_data.append(bullet_image_init_data)
+ border_image_init_data = create_asset_init_data("border", self.scene_width, WINDOW_HEIGHT,
+ path.join(IMAGE_DIR, "border.png"),
+ f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/border.svg")
+ init_image_data.append(border_image_init_data)
+ for player in self.all_players:
+ if isinstance(player, Player):
+ data = player.get_obj_init_data()
+ init_image_data.append(data[0])
+ init_image_data.append(data[1])
+ break
+ for gun in self.guns:
+ if isinstance(gun, Gun):
+ data = gun.get_obj_init_data()
+ init_image_data.append(data[0])
+ init_image_data.append(data[1])
+ break
+ for i in range(1, 4):
+ team_a_lives = "team_a_lives"
+ team_a_lives_image_init_data = create_asset_init_data(f"{team_a_lives}_{i}", LIVES_SIZE[0], LIVES_SIZE[1],
+ path.join(IMAGE_DIR, f"{team_a_lives}_{i}.png"),
+ f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{team_a_lives}_{i}.svg")
+ init_image_data.append(team_a_lives_image_init_data)
+ team_b_lives = "team_b_lives"
+ team_b_lives_image_init_data = create_asset_init_data(f"{team_b_lives}_{i}", LIVES_SIZE[0], LIVES_SIZE[1],
+ path.join(IMAGE_DIR, f"{team_b_lives}_{i}.png"),
+ f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/{team_b_lives}_{i}.svg")
+ init_image_data.append(team_b_lives_image_init_data)
+ return init_image_data
+
+ def get_toggle_progress_data(self):
+ toggle_data = []
+ hourglass_index = 0
+ if self.is_manual:
+ hourglass_index = self.used_frame // 10 % 15
+ toggle_data.append(
+ create_image_view_data(image_id=f"hourglass_{hourglass_index}", x=0, y=2, width=20, height=20, angle=0))
+ x = 23
+ y = 8
+ for frame in range((self.frame_limit - self.used_frame) // int((30 * 2))):
+ toggle_data.append(create_rect_view_data("frame", x, y, 3, 10, RED))
+ x += 3.5
+ toggle_data.append(create_text_view_data(f"Frame: {self.frame_limit - self.used_frame}",
+ self.width_center + self.width_center // 2 + 85, 8, RED,
+ "24px Arial BOLD"))
+ x = 24
+ y = 20
+ for score in range(min(self.team_green_score, self.team_blue_score)):
+ toggle_data.append(create_rect_view_data(name="score", x=x, y=y, width=1, height=10, color=ORANGE))
+ x += 1.5
+ if x > self.width_center:
+ if y == 32:
+ y = 44
+ else:
+ y = 32
+ x = 24
+ for score in range(abs(self.team_green_score - self.team_blue_score)):
+ if self.team_green_score > self.team_blue_score:
+ toggle_data.append(create_rect_view_data("score", x, y, 1, 10, DARKGREEN))
+ else:
+ toggle_data.append(create_rect_view_data("score", x, y, 1, 10, BLUE))
+ x += 1.5
+ if x > self.width_center:
+ if y == 32:
+ y = 44
+ else:
+ y = 32
+ x = 24
+ # 1P
+ x = WINDOW_WIDTH - 125
+ y = WINDOW_HEIGHT - 40
+ toggle_data.append(create_text_view_data(f"Score: {self.team_green_score}", x, y, DARKGREEN, "24px Arial BOLD"))
+ # 2P
+ x = 5
+ y = WINDOW_HEIGHT - 40
+ toggle_data.append(create_text_view_data(f"Score: {self.team_blue_score}", x, y, BLUE, "24px Arial BOLD"))
+ for player in self.all_players:
+ if isinstance(player, Player) and player.is_alive:
+ # lives
+ team_id = "team_a_lives" if player.id == 1 else "team_b_lives"
+ color = DARKGREEN if player.id == 1 else BLUE
+ x = player.play_rect_area.midbottom[0] + 7 + (player.no - 1) * 60 if player.id == 1 \
+ else player.play_rect_area.midbottom[0] - (player.no - self.green_team_num) * 60
+ y = player.play_rect_area.height + 73
+ toggle_data.append(
+ create_text_view_data(f"{player.no}P", x - 5, y - 25, color, "22px Arial BOLD"))
+ for live in range(1, player.lives + 1):
+ toggle_data.append(create_image_view_data(f"{team_id}_{live}", x, y, LIVES_SIZE[0], LIVES_SIZE[1]))
+ x += 10
+ y -= 10
+ return toggle_data
+
+ def get_toggle_with_bias_data(self):
+ toggle_with_bias_data = []
+ color = WHITE
+ for player in self.all_players:
+ if isinstance(player, Player) and player.is_alive:
+ # number
+ if player.no > self.green_team_num:
+ color = WHITE
+ x = player.rect.x
+ y = player.rect.y - 18
+ toggle_with_bias_data.append(create_text_view_data(f"{player.no}P", x, y, color, "16px Arial BOLD"))
+ team_id = "team_a"
+ if player.no > self.green_team_num:
+ team_id = "team_b"
+ # oil
+ y = player.rect.bottom
+ toggle_with_bias_data.append(
+ create_rect_view_data(f"{team_id}_oil", x, y, int(player.oil * 0.5), 8, ORANGE))
+ # power
+ y = player.rect.bottom + 10
+ for power in range(player.power):
+ toggle_with_bias_data.append(create_rect_view_data(f"{team_id}_power", x + 1, y, 3, 8, BLUE))
+ x += 5
+
+ return toggle_with_bias_data
+
+ def get_ai_data_to_player(self):
+ to_player_data = {}
+ num = 0
+ competitor_info = {
+ 1: [player.get_data_from_obj_to_game() for player in self.players_a if isinstance(player, Player)]
+ , 2: [player.get_data_from_obj_to_game() for player in self.players_b if isinstance(player, Player)]
+ }
+ walls_info = [wall.get_data_from_obj_to_game() for wall in self.walls if isinstance(wall, Wall)]
+ bullet_stations_info = [bullst_station.get_data_from_obj_to_game() for bullst_station in self.bullet_stations if
+ isinstance(bullst_station, Station)]
+ oil_stations_info = [oil_station.get_data_from_obj_to_game() for oil_station in self.oil_stations if
+ isinstance(oil_station, Station)]
+ bullets_info = [bullet.get_data_from_obj_to_game() for bullet in self.bullets if
+ isinstance(bullet, Bullet)]
+ for player in self.players_a:
+ if isinstance(player, Player):
+ to_game_data = player.get_data_from_obj_to_game()
+ to_game_data["used_frame"] = self.used_frame
+ to_game_data["status"] = self.status
+ to_game_data["teammate_info"] = competitor_info[1]
+ to_game_data["competitor_info"] = competitor_info[2]
+ to_game_data["walls_info"] = walls_info
+ to_game_data["bullets_info"] = bullets_info
+ to_game_data["bullet_stations_info"] = bullet_stations_info
+ to_game_data["oil_stations_info"] = oil_stations_info
+ to_player_data[get_ai_name(num)] = to_game_data
+ num += 1
+ for player in self.players_b:
+ if isinstance(player, Player):
+ to_game_data = player.get_data_from_obj_to_game()
+ to_game_data["used_frame"] = self.used_frame
+ to_game_data["status"] = self.status
+ to_game_data["teammate_info"] = competitor_info[2]
+ to_game_data["competitor_info"] = competitor_info[1]
+ to_game_data["walls_info"] = walls_info
+ to_game_data["bullets_info"] = bullets_info
+ to_game_data["bullet_stations_info"] = bullet_stations_info
+ to_game_data["oil_stations_info"] = oil_stations_info
+ to_player_data[get_ai_name(num)] = to_game_data
+ num += 1
+
+ return to_player_data
+
+ def get_bgm_data(self):
+ return create_bgm_data("BGM.ogg", 0.1)
+
+ def get_sound_data(self):
+ return [create_sounds_data("shoot", "shoot.wav")
+ , create_sounds_data("touch", "touch.wav")]
+
+ def add_player_score(self, player_no: int, score: int):
+ if not player_no or not score:
+ return
+ for player in self.all_players:
+ if isinstance(player, Player) and player_no == player.no and player.lives >= 0:
+ add_score(player, score)
+
+ def debugging(self, is_debug: bool):
+ self.obj_rect_list = []
+ if not is_debug:
+ return
+ play_rect_area_points = [self.play_rect_area.topleft, self.play_rect_area.topright
+ , self.play_rect_area.bottomright, self.play_rect_area.bottomleft
+ , self.play_rect_area.topleft]
+
+ for sprite in self.all_sprites:
+ if isinstance(sprite, pygame.sprite.Sprite):
+ top_left = sprite.rect.topleft
+ points = [top_left, sprite.rect.topright, sprite.rect.bottomright
+ , sprite.rect.bottomleft, top_left]
+ for index in range(len(points) - 1):
+ self.obj_rect_list.append(create_line_view_data("rect", *points[index], *points[index + 1], RED))
+ self.obj_rect_list.append(create_line_view_data("play_rect_area", *play_rect_area_points[index]
+ , *play_rect_area_points[index + 1], RED))
diff --git a/TankMan/src/Wall.py b/TankMan/src/Wall.py
new file mode 100644
index 0000000..e098f8b
--- /dev/null
+++ b/TankMan/src/Wall.py
@@ -0,0 +1,41 @@
+from os import path
+
+import pygame
+from mlgame.view.view_model import create_asset_init_data, create_image_view_data
+
+from .env import IMAGE_DIR, WALL_LIVE
+
+
+class Wall(pygame.sprite.Sprite):
+ def __init__(self, construction, **kwargs):
+ super().__init__()
+ self.id = construction["_id"]
+ self.no = 0
+ self.rect = pygame.Rect(construction["_init_pos"], construction["_init_size"])
+ self.angle = 0
+ self.is_alive = True
+ self.lives = WALL_LIVE
+
+ def update(self, *args, **kwargs) -> None:
+ if self.lives <= 0:
+ self.kill()
+
+ def collide_with_bullets(self):
+ if self.lives > 0:
+ self.lives -= 1
+
+ def get_data_from_obj_to_game(self):
+ info = {"id": f"wall_{self.lives}", "x": self.rect.x, "y": self.rect.y, "lives": self.lives}
+ return info
+
+ def get_obj_progress_data(self):
+ return create_image_view_data(f"wall_{self.lives}", self.rect.x, self.rect.y
+ , self.rect.width, self.rect.height, self.angle)
+
+ def get_obj_init_data(self):
+ image_init_data = []
+ for i in range(1, self.lives+1):
+ image_init_data.append(create_asset_init_data(f"wall_{i}", self.rect.width, self.rect.height,
+ path.join(IMAGE_DIR, f"wall_{min(i,3)}.png"),
+ f"https://raw.githubusercontent.com/Jesse-Jumbo/TankMan/main/asset/image/wall_{min(i,3)}.png"))
+ return image_init_data
diff --git a/TankMan/src/__init__.py b/TankMan/src/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/TankMan/src/collide_hit_rect.py b/TankMan/src/collide_hit_rect.py
new file mode 100644
index 0000000..f71a861
--- /dev/null
+++ b/TankMan/src/collide_hit_rect.py
@@ -0,0 +1,58 @@
+from typing import Optional
+import pygame.sprite
+
+from src.Player import Player
+from src.Bullet import Bullet
+from src.Wall import Wall
+
+
+def collide_with_walls(group1: pygame.sprite.Group, group2: pygame.sprite.Group):
+ hits = pygame.sprite.groupcollide(group1, group2, False, False, pygame.sprite.collide_rect_ratio(0.8))
+ for sprite, walls in hits.items():
+ sprite.collide_with_walls()
+
+
+def collide_with_bullets(group1: pygame.sprite.Group, group2: pygame.sprite.Group, green_team_num: Optional[int] = None):
+ hits = pygame.sprite.groupcollide(group1, group2, False, False, pygame.sprite.collide_rect_ratio(0.8))
+ player_score_data = {}
+ for sprite, bullets in hits.items():
+ for bullet in bullets:
+ if bullet.no != sprite.no and sprite.lives > 0:
+ bullet.kill()
+
+ if isinstance(sprite, Player):
+ assert green_team_num is not None
+ if (bullet.no - 1) // green_team_num == (sprite.no - 1) // green_team_num:
+ # -20 for friendly damage
+ score = -20
+ else:
+ score = 20
+ elif isinstance(sprite, Wall):
+ score = 1
+ if sprite.lives == 1:
+ score += 5
+ else:
+ continue
+
+ sprite.collide_with_bullets()
+
+ if player_score_data.get(bullet.no) is None:
+ player_score_data[bullet.no] = 0
+ player_score_data[bullet.no] += score
+ return player_score_data
+
+
+def collide_with_supply_stations(sprites: pygame.sprite.Group, supply_stations: pygame.sprite.Group):
+ hits = pygame.sprite.groupcollide(sprites, supply_stations, False, False, pygame.sprite.collide_rect_ratio(0.8))
+ for sprite, supply_station in hits.items():
+ if isinstance(sprite, Player):
+ if supply_station[0].id == 5:
+ sprite.get_oil(supply_station[0].power)
+ else:
+ sprite.get_power(supply_station[0].power)
+ elif isinstance(sprite, Bullet):
+ sprite.kill()
+
+ supply_station[0].collect()
+
+ return [supply_station[0] for supply_station in hits.values()]
diff --git a/TankMan/src/env.py b/TankMan/src/env.py
new file mode 100644
index 0000000..00f422f
--- /dev/null
+++ b/TankMan/src/env.py
@@ -0,0 +1,85 @@
+from os import path
+
+import pygame
+
+IS_DEBUG = False
+# TODO remove width and height setting
+'''width and height'''
+WINDOW_WIDTH = 1000
+WINDOW_HEIGHT = 700
+
+'''environment data'''
+FPS = 30
+SHOOT_COOLDOWN = 15
+
+'''color'''
+BLACK = "#000000"
+WHITE = "#ffffff"
+RED = "#ff0000"
+YELLOW = "#ffff00"
+GREEN = "#00ff00"
+DARKGREEN = "#006400"
+GREY = "#8c8c8c"
+BLUE = "#0000ff"
+LIGHT_BLUE = "#21A1F1"
+CYAN_BLUE = "#00FFFF"
+PINK = "#FF00FF"
+DARKGREY = "#282828"
+LIGHTGREY = "#646464"
+BROWN = "#643705"
+FOREST = "#22390A"
+MAGENTA = "#FF00FF"
+MEDGRAY = "#4B4B4B"
+ORANGE = "#FFA500"
+
+'''command'''
+TURN_LEFT_CMD = "TURN_LEFT"
+TURN_RIGHT_CMD = "TURN_RIGHT"
+FORWARD_CMD = "FORWARD"
+BACKWARD_CMD = "BACKWARD"
+AIM_LEFT_CMD = "AIM_LEFT"
+AIM_RIGHT_CMD = "AIM_RIGHT"
+SHOOT = "SHOOT"
+
+'''data path'''
+GAME_DIR = path.dirname(__file__)
+IMAGE_DIR = path.join(GAME_DIR, "..", "asset", "image")
+SOUND_DIR = path.join(GAME_DIR, "..", "asset", "sound")
+MAP_DIR = path.join(GAME_DIR, "..", "asset", 'maps')
+
+'''BG View'''
+TITLE = "TankMan!"
+BG_COLOR = DARKGREY
+TILE_X_SIZE = 50
+TILE_Y_SIZE = 50
+TILE_SIZE = 50
+TEXT_SIZE = 100
+
+'''object size'''
+ALL_OBJECT_SIZE = pygame.Rect(0, 0, 50, 50)
+BULLET_SIZE = (13, 16)
+LIVES_SIZE = (30, 25)
+
+"""all setting"""
+DOWN_IMG = 'down'
+RIGHT_IMG = 'right'
+UP_IMG = 'up'
+LEFT_IMG = 'left'
+
+"""collide setting"""
+WITH_PLAYER = 'player'
+
+"""map data numbers"""
+PLAYER_1_IMG_NO = 1
+PLAYER_2_IMG_NO = 2
+WALL_IMG_NO = 3
+BULLET_STATION_IMG_NO = 4
+OIL_STATION_IMG_NO = 5
+WALL_LIVE = 4
+BULLET_SPEED = 30
+BULLET_TRAVEL_DISTANCE = 300
+
+"""music"""
+BGM = 'background_music.ogg/.wav/.mp3'
+MENU_SND = 'MenuTheme.ogg/.wav/.mp3'
+
diff --git a/TankMan/src/game_module/SoundController.py b/TankMan/src/game_module/SoundController.py
new file mode 100644
index 0000000..70ad78b
--- /dev/null
+++ b/TankMan/src/game_module/SoundController.py
@@ -0,0 +1,44 @@
+from os import path
+
+import pygame.mixer
+
+
+def create_sounds_data(id: str, name: str):
+ return {
+ "_id": id
+ , "_name": name
+ }
+
+
+def create_bgm_data(name: str, volume: float):
+ return {
+ "_name": name
+ , "_volume": volume
+ }
+
+
+class SoundController:
+ def __init__(self, sound_path: str, sounds_data_list: list):
+ self._sound_path = sound_path
+ if not self._sound_path:
+ return
+ self._sounds_obj = {}
+ pygame.mixer.init()
+ for sounds_data in sounds_data_list:
+ sound_data = path.join(self._sound_path, sounds_data["_name"])
+ self._sounds_obj[sounds_data["_id"]] = pygame.mixer.Sound(sound_data)
+
+ def play_music(self, bgm_data: dict) -> None:
+ if not self._sound_path:
+ return
+ pygame.mixer.init()
+ pygame.mixer.music.load(path.join(self._sound_path, bgm_data["_name"]))
+ pygame.mixer.music.set_volume(bgm_data["_volume"])
+ pygame.mixer.music.play(-1)
+
+ def play_sound(self, id: str, volume: float, maz_time: int) -> None:
+ if not self._sound_path:
+ return
+ sound_obj = self._sounds_obj[id]
+ sound_obj.set_volume(volume)
+ sound_obj.play(maxtime=maz_time)
diff --git a/TankMan/src/game_module/TiledMap.py b/TankMan/src/game_module/TiledMap.py
new file mode 100644
index 0000000..7652b5d
--- /dev/null
+++ b/TankMan/src/game_module/TiledMap.py
@@ -0,0 +1,68 @@
+import pytmx
+
+
+def create_construction(_id: int or str, _no: int, _init_pos: tuple, _init_size: tuple):
+ return {
+ "_id": _id
+ , "_no": _no
+ , "_init_pos": _init_pos
+ , "_init_size": _init_size
+ }
+
+
+# Map 讀取地圖資料
+class TiledMap:
+ def __init__(self, filepath: str):
+ tm = pytmx.TiledMap(filepath)
+ self.tile_width = tm.tilewidth
+ self.tile_height = tm.tileheight
+ self.width = tm.width
+ self.height = tm.height
+ self.map_width = self.tile_width * self.width
+ self.map_height = self.tile_height * self.height
+ self.tmx_data = tm
+ self._is_record = False
+ self.all_pos_list = []
+ self.empty_pos_list = []
+ self.empty_quadrant_pos_dict = {1: [], 2: [], 3: [], 4: []}
+ self.all_obj_data_dict = {}
+ # TODO refactor
+ self.all_obj = {}
+
+ def add_init_obj_data(self, img_id: int, cls, **kwargs):
+ obj_data = {img_id: {"cls": cls,
+ "kwargs": kwargs
+ }
+ }
+ self.all_obj_data_dict.update(obj_data)
+ self.all_obj[img_id] = []
+
+ def create_init_obj_dict(self) -> dict:
+ obj_no = 0
+ for layer in self.tmx_data.visible_layers:
+ for x, y, gid, in layer:
+ if isinstance(layer, pytmx.TiledTileLayer):
+ pos = (x * self.tile_width, y * self.tile_height)
+ if not self._is_record:
+ self.all_pos_list.append(pos)
+ if not self._is_record and not gid: # 0代表空格,無圖塊
+ self.empty_pos_list.append(pos)
+ if pos[0] >= self.map_width // 2 and pos[1] < self.map_height // 2:
+ self.empty_quadrant_pos_dict[1].append(pos)
+ elif pos[0] < self.map_width // 2 and pos[1] < self.map_height // 2:
+ self.empty_quadrant_pos_dict[2].append(pos)
+ elif pos[0] < self.map_width // 2 and pos[1] >= self.map_height // 2:
+ self.empty_quadrant_pos_dict[3].append(pos)
+ else:
+ self.empty_quadrant_pos_dict[4].append(pos)
+ elif gid:
+ img_id = layer.parent.tiledgidmap[gid]
+ kwargs = self.all_obj_data_dict[img_id]["kwargs"]
+ obj_no += 1
+ img_info = {"_id": img_id, "_no": obj_no
+ , "_init_pos": pos
+ , "_init_size": (self.tile_width, self.tile_height)
+ }
+ self.all_obj[img_id].append(self.all_obj_data_dict[img_id]["cls"](img_info, **kwargs))
+ self._is_record = True
+ return self.all_obj
diff --git a/TankMan/src/game_module/__init__.py b/TankMan/src/game_module/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/TankMan/src/game_module/fuctions.py b/TankMan/src/game_module/fuctions.py
new file mode 100644
index 0000000..1d3d1f8
--- /dev/null
+++ b/TankMan/src/game_module/fuctions.py
@@ -0,0 +1,26 @@
+import pygame.sprite
+
+
+def get_size(sprite: pygame.sprite.Sprite):
+ return sprite.rect.width, sprite.rect.height
+
+
+def set_topleft(sprite: pygame.sprite.Sprite, top_left: tuple):
+ sprite.rect.topleft = top_left
+
+
+def add_score(sprite: pygame.sprite.Sprite, score: int):
+ sprite.score += score
+
+
+def set_shoot(sprite: pygame.sprite.Sprite, is_shoot: bool):
+ sprite.is_shoot = is_shoot
+
+
+def get_sprites_progress_data(sprites: pygame.sprite.Group):
+ data_list = []
+ for sprite in sprites:
+ data = sprite.get_obj_progress_data()
+ if data:
+ data_list.append(data)
+ return data_list
diff --git a/TankMan/src/game_module/test_case.py b/TankMan/src/game_module/test_case.py
new file mode 100644
index 0000000..156a1b8
--- /dev/null
+++ b/TankMan/src/game_module/test_case.py
@@ -0,0 +1,11 @@
+from src.template.Player import Player
+
+
+class TestPlayer(object):
+ construction = {"_id": 0, "_no": 0, "x": 0, "y": 0, "width": 60, "height": 60}
+ player = Player(construction)
+
+ def test_get_xy_pos(self):
+ assert type(self.player.get_xy_pos()) == tuple
+ assert type(self.player.get_xy_pos()[0]) == int
+ assert type(self.player.get_xy_pos()[1]) == int
diff --git a/TankMan/test/__init__.py b/TankMan/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/TankMan/test/test_case.py b/TankMan/test/test_case.py
new file mode 100644
index 0000000..1e1dbda
--- /dev/null
+++ b/TankMan/test/test_case.py
@@ -0,0 +1,242 @@
+from os import path
+
+from mlgame.view.view_model import create_asset_init_data, create_image_view_data
+from TankBattleMode import TankBattleMode
+from TankPlayer import TankPlayer
+from env import MAP_DIR
+
+
+class TestTankBattleMode(object):
+ map_path = path.join(MAP_DIR, "map_00.tmx")
+ mode = TankBattleMode(map_path, 1200, True)
+
+ def test_calculate_score_type(self):
+ assert type(self.mode.calculate_score()) == tuple
+ assert type(self.mode.calculate_score()[0]) == int
+ assert type(self.mode.calculate_score()[1]) == int
+
+ def test_is_list_type_of_get_1P_command(self):
+ assert type(self.mode.get_1P_command()) == list
+
+ def test_is_list_type_of_get_2P_command(self):
+ assert type(self.mode.get_2P_command()) == list
+
+ def test_draw_sprite_data(self):
+ sprite_data = self.mode.draw_sprite_data()
+ image_init_data_template = create_image_view_data("1P", 0, 0, 60, 60, 0)
+ for data in sprite_data:
+ assert data.keys() == image_init_data_template.keys()
+ for key in data.keys():
+ assert type(data[key]) == type(image_init_data_template[key])
+
+
+class TestTankPlayer(object):
+ construction = {"_id": 1, "_no": 0, "x": 0, "y": 0, "width": 60, "height": 60}
+ player = TankPlayer(construction)
+
+ # TODO how to test update
+ def test_update(self):
+ pass
+
+ # TODO how to test rotate
+ def test_rotate(self):
+ pass
+
+ def get_shoot_info(self):
+ return {"id": 0, "center_pos": (0, 0), "rot": 0}
+
+ def test_get_player_shoot_info(self):
+ shoot_info = self.player.create_shoot_info()
+ assert shoot_info.keys() == self.get_shoot_info().keys()
+ for key in shoot_info.keys():
+ assert type(shoot_info[key]) == type(self.get_shoot_info()[key])
+
+ def test_arg(self):
+ assert self.player.origin_size == (60, 60)
+ assert self.player.speed == 8
+ assert type(self.player.angle) == int
+ assert type(self.player.score) == int
+ assert type(self.player.used_frame) == int
+ assert type(self.player.rot) == int
+ assert self.player.rot_speed == 45
+ assert 100 >= self.player.shield > 0
+ assert 10 >= self.player.power > 0
+ assert 100 >= self.player.oil > 0
+ assert type(self.player.last_turn_frame) == int
+ assert type(self.player.is_shoot) == bool
+ assert self.player.is_alive
+ assert type(self.player.is_turn) == bool
+ assert type(self.player.is_forward) == bool
+ assert type(self.player.is_backward) == bool
+
+ def test_move_arg(self):
+ move = {"left_up": (-self.player.speed, -self.player.speed),
+ "right_up": (self.player.speed, -self.player.speed),
+ "left_down": (-self.player.speed, self.player.speed),
+ "right_down": (self.player.speed, self.player.speed),
+ "left": (-self.player.speed, 0), "right": (self.player.speed, 0), "up": (0, -self.player.speed),
+ "down": (0, self.player.speed)}
+ assert self.player.move_dict == move
+
+ def test_act(self):
+ # TODO 增加限制type
+ # TODO how test shoot act
+ self.player.act(["SHOOT"])
+ # assert self.player.is_shoot and self.player.power == 9 and self.player.last_shoot_frame == 50
+ self.player.act(["TURN_LEFT"])
+ assert self.player.is_turn and round(self.player.oil, 1) == 99.9
+ self.player.act(["TURN_RIGHT"])
+ assert self.player.is_turn and round(self.player.oil, 1) == 99.8
+ self.player.act(["FORWARD"])
+ assert self.player.is_forward and not self.player.is_backward and round(self.player.oil, 1) == 99.7
+ self.player.act(["BACKWARD"])
+ assert self.player.is_backward and not self.player.is_forward and round(self.player.oil, 1) == 99.6
+
+ def go_straight(self, forward_or_backward: str):
+ self.player.rot = 0
+ self.player.hit_rect.center = self.player.origin_center
+ if forward_or_backward == "forward":
+ self.player.forward()
+ else:
+ self.player.backward()
+ return self.player.origin_center, self.player.hit_rect.center
+
+ # TODO how better
+ def test_go_straight(self):
+ speed = self.player.speed
+ origin_center, now_center = self.go_straight("forward")
+ assert (origin_center[0]-speed, origin_center[1]) == now_center
+ origin_center, now_center = self.go_straight("backward")
+ assert (origin_center[0]+speed, origin_center[1]) == now_center
+
+ def turn(self, right_or_left: str, is_turn: bool):
+ origin_rot = self.player.rot
+ self.player.is_turn = is_turn
+ self.player.last_turn_frame = 500
+ if right_or_left == "right":
+ self.player.turn_right()
+ else:
+ self.player.turn_left()
+ return self.player.last_turn_frame, origin_rot, self.player.rot, self.player.is_turn
+
+ # TODO how better
+ def test_turn_method(self):
+ turn_cd, origin_rot, new_rot, is_turn = self.turn("right", True)
+ assert turn_cd == 500 and new_rot == origin_rot and is_turn
+ turn_cd, origin_rot, new_rot, is_turn = self.turn("right", False)
+ assert turn_cd == 0 and new_rot == origin_rot-self.player.rot_speed and is_turn
+ turn_cd, origin_rot, new_rot, is_turn = self.turn("left", True)
+ assert turn_cd == 500 and new_rot == origin_rot and is_turn
+ turn_cd, origin_rot, new_rot, is_turn = self.turn("left", False)
+ assert turn_cd == 0 and new_rot == origin_rot+self.player.rot_speed and is_turn
+
+ def collide_with_walls(self, is_forward: bool):
+ self.player.rot = 0
+ self.player.hit_rect.center = (100, 100)
+ old_center = self.player.hit_rect.center
+ self.player.is_forward = is_forward
+ self.player.collide_with_walls()
+ return old_center, self.player.hit_rect.center
+
+ def test_collide_with_walls(self):
+ speed = self.player.speed
+ old_center, new_center = self.collide_with_walls(is_forward=True)
+ assert (old_center[0]+speed, old_center[1]) == new_center
+ old_center, new_center = self.collide_with_walls(is_forward=False)
+ assert (old_center[0]-speed, old_center[1]) == new_center
+
+ def collide_with_bullets(self, shield: int, lives: int):
+ self.player.shield = shield
+ self.player.lives = lives
+ self.player.collide_with_bullets()
+ return {"shield": self.player.shield, "lives": self.player.lives, "pos": self.player.rect.center}
+
+ def test_collide_with_bullets(self):
+ self.player.rect.center = (50, 50)
+ self.collide_with_bullets(1, 1)
+ assert self.player.shield == 100
+ assert self.player.lives == 0
+ assert self.player.rect.center == self.player.origin_center
+ self.player.rect.center = (50, 50)
+ self.collide_with_bullets(1, 2)
+ assert self.player.shield == 100
+ assert self.player.lives == 1
+ assert self.player.rect.center == self.player.origin_center
+ self.player.rect.center = (50, 50)
+ self.collide_with_bullets(100, 2)
+ assert 100 > self.player.shield > 89
+ assert self.player.lives == 2
+ assert self.player.rect.center == (50, 50)
+ assert self.player.rect.center != self.player.origin_center
+
+ def test_get_power(self):
+ self.player.get_power(-10)
+ assert self.player.power == 0
+ self.player.get_power(10)
+ assert self.player.power == 10
+ self.player.get_power(-11)
+ assert self.player.power == 0
+ self.player.get_power(11)
+ assert self.player.power == 10
+ self.player.get_power(-5)
+ assert self.player.power == 5
+ self.player.get_power(3)
+ assert self.player.power == 8
+
+ def test_get_oil(self):
+ self.player.get_oil(-100)
+ assert self.player.oil == 0
+ self.player.get_oil(100)
+ assert self.player.oil == 100
+ self.player.get_oil(-110)
+ assert self.player.oil == 0
+ self.player.get_oil(110)
+ assert self.player.oil == 100
+ self.player.get_oil(-50)
+ assert self.player.oil == 50
+ self.player.get_oil(30)
+ assert self.player.oil == 80
+
+ def get_info(self):
+ return {"id": f"{0}P",
+ "x": 0,
+ "y": 0,
+ "speed": 0,
+ "score": 0,
+ "power": 0,
+ "oil": 0,
+ "shield": 0,
+ "lives": 0
+ }
+
+ def test_get_player_info(self):
+ info = self.player.get_info()
+ assert info.keys() == self.get_info().keys()
+ for key in info.keys():
+ assert type(info[key]) == type(self.get_info()[key])
+
+ def get_result(self):
+ return {"id": f"{0}P", "x": 0, "y": 0, "score": 0, "shield": 0, "lives": 0}
+
+ def test_get_result(self):
+ result = self.player.get_result()
+ assert result.keys() == self.get_result().keys()
+ for key in result.keys():
+ assert type(result[key]) == type(self.get_result()[key])
+
+ def get_image_data(self):
+ return {ID: f"{0}P", X: 0, Y: 0, WIDTH: 0, HEIGHT: 0, ANGLE: 0}
+
+ def test_get_image_data(self):
+ image_data = self.player.get_image_data()
+ assert image_data.keys() == self.get_image_data().keys()
+ for key in image_data.keys():
+ assert type(image_data[key]) == type(self.get_image_data()[key])
+
+ def test_get_image_init_data(self):
+ image_init_data = self.player.get_image_init_data()
+ image_init_data_template = create_asset_init_data("1P", 60, 60, "path", "url")
+ for data in image_init_data:
+ assert data.keys() == image_init_data_template.keys()
+ for key in data.keys():
+ assert type(data[key]) == type(image_init_data_template[key])
diff --git a/justfile b/justfile
new file mode 100644
index 0000000..47b86d5
--- /dev/null
+++ b/justfile
@@ -0,0 +1,19 @@
+test:
+ just release
+ ./test.sh
+clean:
+ rm -r output1/* output2/*
+train epoch:
+ just build
+ ./train.sh {{epoch}}
+init:
+ just build
+ MODEL_PATH="output1" ./train
+ MODEL_PATH="output2" ./train
+build:
+ cd tank-rust && cargo build --release --lib --bin tank-rust
+ cp tank-rust/target/release/tank-rust train
+ cp tank-rust/target/release/libtank_rust.so .
+release:
+ cd tank-rust && cargo build --release --lib --no-default-features
+ cp tank-rust/target/release/libtank_rust.so .
\ No newline at end of file
diff --git a/ml/collect1.py b/ml/collect1.py
new file mode 100644
index 0000000..2cda15d
--- /dev/null
+++ b/ml/collect1.py
@@ -0,0 +1,187 @@
+"""
+The template of the main script of the machine learning process
+"""
+
+from ctypes import *
+
+import pygame
+
+
+class Player(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("speed", c_int32),
+ ("score", c_int32),
+ ("power", c_int32),
+ ("oil", c_float),
+ ("lives", c_int32),
+ ("angle", c_int32),
+ ("gun_angle", c_int32),
+ ("cooldown", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.speed = d["speed"]
+ self.score = d["score"]
+ self.power = d["power"]
+ self.oil = d["oil"]
+ self.lives = d["lives"]
+ self.angle = d["angle"]
+ self.gun_angle = d["gun_angle"]
+ self.cooldown = d["cooldown"]
+ return self
+
+
+class Bullet(Structure):
+ _fields_ = [("x", c_int32), ("y", c_int32)]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ return self
+
+
+class Station(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("power", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.power = d["power"]
+ return self
+
+
+class Wall(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("lives", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.lives = d["lives"]
+ return self
+
+
+class Info(Structure):
+ _fields_ = [
+ ("player", POINTER(Player)),
+ ("teammates", POINTER(Player)),
+ ("teammates_len", c_uint32),
+ ("enemies", POINTER(Player)),
+ ("enemies_len", c_uint32),
+ ("bullets", POINTER(Bullet)),
+ ("bullet_len", c_uint32),
+ ("bullet_stations", POINTER(Station)),
+ ("bullet_stations_len", c_uint32),
+ ("oil_stations", POINTER(Station)),
+ ("oil_stations_len", c_uint32),
+ ("walls", POINTER(Wall)),
+ ("walls_len", c_uint32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.player = pointer(Player().from_dict(d))
+
+ self.teammates = (Player * len(d["teammate_info"]))()
+ for i, t in enumerate(d["teammate_info"]):
+ self.teammates[i] = Player().from_dict(t)
+ self.teammates_len = len(d["teammate_info"])
+
+ self.enemies = (Player * len(d["competitor_info"]))()
+ for i, e in enumerate(d["competitor_info"]):
+ self.enemies[i] = Player().from_dict(e)
+ self.enemies_len = len(d["competitor_info"])
+
+ self.bullets = (Bullet * len(d["bullets_info"]))()
+ for i, b in enumerate(d["bullets_info"]):
+ self.bullets[i] = Bullet().from_dict(b)
+ self.bullet_len = len(d["bullets_info"])
+
+ self.bullet_stations = (Station * len(d["bullet_stations_info"]))()
+ for i, b in enumerate(d["bullet_stations_info"]):
+ self.bullet_stations[i] = Station().from_dict(b)
+ self.bullet_stations_len = len(d["bullet_stations_info"])
+
+ self.oil_stations = (Station * len(d["oil_stations_info"]))()
+ for i, o in enumerate(d["oil_stations_info"]):
+ self.oil_stations[i] = Station().from_dict(o)
+ self.oil_stations_len = len(d["oil_stations_info"])
+
+ self.walls = (Wall * len(d["walls_info"]))()
+ for i, w in enumerate(d["walls_info"]):
+ self.walls[i] = Wall().from_dict(w)
+ self.walls_len = len(d["walls_info"])
+
+ return self
+
+
+tank_rs = cdll.LoadLibrary("../libtank_rust.so")
+tank_rs.init.argtypes = [c_char_p, c_int32]
+tank_rs.init.restype = c_void_p
+tank_rs.tick.argtypes = [c_void_p, POINTER(Info)]
+tank_rs.tick.restype = c_int32
+tank_rs.flush.argtypes = [c_void_p]
+
+app = tank_rs.init(c_char_p("../output1".encode()), 10)
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param ai_name A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ if scene_info["status"] != "GAME_ALIVE":
+ tank_rs.flush(app)
+
+ info = Info().from_dict(scene_info)
+
+ action = tank_rs.tick(app, info)
+
+ command = ""
+
+ if action == 0:
+ command = "FORWARD"
+ elif action == 1:
+ command = "BACKWARD"
+ elif action == 2:
+ command = "TURN_RIGHT"
+ elif action == 3:
+ command = "TURN_LEFT"
+ elif action == 4:
+ command = "AIM_RIGHT"
+ elif action == 5:
+ command = "AIM_LEFT"
+ elif action == 6:
+ command = "SHOOT"
+ elif action == 7:
+ command = "NONE"
+
+ info
+ return [command]
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
diff --git a/ml/collect2.py b/ml/collect2.py
new file mode 100644
index 0000000..a5ca329
--- /dev/null
+++ b/ml/collect2.py
@@ -0,0 +1,187 @@
+"""
+The template of the main script of the machine learning process
+"""
+
+from ctypes import *
+
+import pygame
+
+
+class Player(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("speed", c_int32),
+ ("score", c_int32),
+ ("power", c_int32),
+ ("oil", c_float),
+ ("lives", c_int32),
+ ("angle", c_int32),
+ ("gun_angle", c_int32),
+ ("cooldown", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.speed = d["speed"]
+ self.score = d["score"]
+ self.power = d["power"]
+ self.oil = d["oil"]
+ self.lives = d["lives"]
+ self.angle = d["angle"]
+ self.gun_angle = d["gun_angle"]
+ self.cooldown = d["cooldown"]
+ return self
+
+
+class Bullet(Structure):
+ _fields_ = [("x", c_int32), ("y", c_int32)]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ return self
+
+
+class Station(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("power", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.power = d["power"]
+ return self
+
+
+class Wall(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("lives", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.lives = d["lives"]
+ return self
+
+
+class Info(Structure):
+ _fields_ = [
+ ("player", POINTER(Player)),
+ ("teammates", POINTER(Player)),
+ ("teammates_len", c_uint32),
+ ("enemies", POINTER(Player)),
+ ("enemies_len", c_uint32),
+ ("bullets", POINTER(Bullet)),
+ ("bullet_len", c_uint32),
+ ("bullet_stations", POINTER(Station)),
+ ("bullet_stations_len", c_uint32),
+ ("oil_stations", POINTER(Station)),
+ ("oil_stations_len", c_uint32),
+ ("walls", POINTER(Wall)),
+ ("walls_len", c_uint32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.player = pointer(Player().from_dict(d))
+
+ self.teammates = (Player * len(d["teammate_info"]))()
+ for i, t in enumerate(d["teammate_info"]):
+ self.teammates[i] = Player().from_dict(t)
+ self.teammates_len = len(d["teammate_info"])
+
+ self.enemies = (Player * len(d["competitor_info"]))()
+ for i, e in enumerate(d["competitor_info"]):
+ self.enemies[i] = Player().from_dict(e)
+ self.enemies_len = len(d["competitor_info"])
+
+ self.bullets = (Bullet * len(d["bullets_info"]))()
+ for i, b in enumerate(d["bullets_info"]):
+ self.bullets[i] = Bullet().from_dict(b)
+ self.bullet_len = len(d["bullets_info"])
+
+ self.bullet_stations = (Station * len(d["bullet_stations_info"]))()
+ for i, b in enumerate(d["bullet_stations_info"]):
+ self.bullet_stations[i] = Station().from_dict(b)
+ self.bullet_stations_len = len(d["bullet_stations_info"])
+
+ self.oil_stations = (Station * len(d["oil_stations_info"]))()
+ for i, o in enumerate(d["oil_stations_info"]):
+ self.oil_stations[i] = Station().from_dict(o)
+ self.oil_stations_len = len(d["oil_stations_info"])
+
+ self.walls = (Wall * len(d["walls_info"]))()
+ for i, w in enumerate(d["walls_info"]):
+ self.walls[i] = Wall().from_dict(w)
+ self.walls_len = len(d["walls_info"])
+
+ return self
+
+
+tank_rs = cdll.LoadLibrary("../libtank_rust.so")
+tank_rs.init.argtypes = [c_char_p, c_int32]
+tank_rs.init.restype = c_void_p
+tank_rs.tick.argtypes = [c_void_p, POINTER(Info)]
+tank_rs.tick.restype = c_int32
+tank_rs.flush.argtypes = [c_void_p]
+
+app = tank_rs.init(c_char_p("../output2".encode()), 10)
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param ai_name A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ if scene_info["status"] != "GAME_ALIVE":
+ tank_rs.flush(app)
+
+ info = Info().from_dict(scene_info)
+
+ action = tank_rs.tick(app, info)
+
+ command = ""
+
+ if action == 0:
+ command = "FORWARD"
+ elif action == 1:
+ command = "BACKWARD"
+ elif action == 2:
+ command = "TURN_RIGHT"
+ elif action == 3:
+ command = "TURN_LEFT"
+ elif action == 4:
+ command = "AIM_RIGHT"
+ elif action == 5:
+ command = "AIM_LEFT"
+ elif action == 6:
+ command = "SHOOT"
+ elif action == 7:
+ command = "NONE"
+
+ info
+ return [command]
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
diff --git a/ml/test.py b/ml/test.py
new file mode 100644
index 0000000..5552372
--- /dev/null
+++ b/ml/test.py
@@ -0,0 +1,202 @@
+"""
+The template of the main script of the machine learning process
+"""
+
+from ctypes import *
+
+import pygame
+
+
+class Player(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("speed", c_int32),
+ ("score", c_int32),
+ ("power", c_int32),
+ ("oil", c_float),
+ ("lives", c_int32),
+ ("angle", c_int32),
+ ("gun_angle", c_int32),
+ ("cooldown", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.speed = d["speed"]
+ self.score = d["score"]
+ self.power = d["power"]
+ self.oil = d["oil"]
+ self.lives = d["lives"]
+ self.angle = d["angle"]
+ self.gun_angle = d["gun_angle"]
+ self.cooldown = d["cooldown"]
+ return self
+
+
+class Bullet(Structure):
+ _fields_ = [("x", c_int32), ("y", c_int32)]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ return self
+
+
+class Station(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("power", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.power = d["power"]
+ return self
+
+
+class Wall(Structure):
+ _fields_ = [
+ ("x", c_int32),
+ ("y", c_int32),
+ ("lives", c_int32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.x = d["x"]
+ self.y = d["y"]
+ self.lives = d["lives"]
+ return self
+
+
+class Info(Structure):
+ _fields_ = [
+ ("player", POINTER(Player)),
+ ("teammates", POINTER(Player)),
+ ("teammates_len", c_uint32),
+ ("enemies", POINTER(Player)),
+ ("enemies_len", c_uint32),
+ ("bullets", POINTER(Bullet)),
+ ("bullet_len", c_uint32),
+ ("bullet_stations", POINTER(Station)),
+ ("bullet_stations_len", c_uint32),
+ ("oil_stations", POINTER(Station)),
+ ("oil_stations_len", c_uint32),
+ ("walls", POINTER(Wall)),
+ ("walls_len", c_uint32),
+ ]
+
+ def from_dict(self, d: dict):
+ self.player = pointer(Player().from_dict(d))
+
+ self.teammates = (Player * len(d["teammate_info"]))()
+ for i, t in enumerate(d["teammate_info"]):
+ self.teammates[i] = Player().from_dict(t)
+ self.teammates_len = len(d["teammate_info"])
+
+ self.enemies = (Player * len(d["competitor_info"]))()
+ for i, e in enumerate(d["competitor_info"]):
+ self.enemies[i] = Player().from_dict(e)
+ self.enemies_len = len(d["competitor_info"])
+
+ self.bullets = (Bullet * len(d["bullets_info"]))()
+ for i, b in enumerate(d["bullets_info"]):
+ self.bullets[i] = Bullet().from_dict(b)
+ self.bullet_len = len(d["bullets_info"])
+
+ self.bullet_stations = (Station * len(d["bullet_stations_info"]))()
+ for i, b in enumerate(d["bullet_stations_info"]):
+ self.bullet_stations[i] = Station().from_dict(b)
+ self.bullet_stations_len = len(d["bullet_stations_info"])
+
+ self.oil_stations = (Station * len(d["oil_stations_info"]))()
+ for i, o in enumerate(d["oil_stations_info"]):
+ self.oil_stations[i] = Station().from_dict(o)
+ self.oil_stations_len = len(d["oil_stations_info"])
+
+ self.walls = (Wall * len(d["walls_info"]))()
+ for i, w in enumerate(d["walls_info"]):
+ self.walls[i] = Wall().from_dict(w)
+ self.walls_len = len(d["walls_info"])
+
+ return self
+
+
+tank_rs = cdll.LoadLibrary("../libtank_rust.so")
+tank_rs.init.argtypes = [c_char_p, c_int32]
+tank_rs.init.restype = c_void_p
+tank_rs.tick.argtypes = [c_void_p, POINTER(Info)]
+tank_rs.tick.restype = c_int32
+tank_rs.flush.argtypes = [c_void_p]
+
+app = tank_rs.init(c_char_p("../output2".encode()), 10)
+
+
+class MLPlay:
+ def __init__(self, ai_name, *args, **kwargs):
+ """
+ Constructor
+
+ @param ai_name A string "1P" or "2P" indicates that the `MLPlay` is used by
+ which side.
+ """
+ self.side = ai_name
+ print(f"Initial Game {ai_name} ml script")
+ self.time = 0
+
+ def update(self, scene_info: dict, keyboard=[], *args, **kwargs):
+ """
+ Generate the command according to the received scene information
+ """
+ if scene_info["status"] != "GAME_ALIVE":
+ tank_rs.flush(app)
+
+ command = "command"
+
+ if pygame.K_d in keyboard:
+ command = "TURN_RIGHT"
+ elif pygame.K_a in keyboard:
+ command = "TURN_LEFT"
+ elif pygame.K_w in keyboard:
+ command = "FORWARD"
+ elif pygame.K_s in keyboard:
+ command = "BACKWARD"
+
+ if pygame.K_q in keyboard:
+ command = "AIM_LEFT"
+ elif pygame.K_e in keyboard:
+ command = "AIM_RIGHT"
+
+ if pygame.K_f in keyboard:
+ command = "SHOOT"
+ if command == "command":
+ info = Info().from_dict(scene_info)
+
+ action = tank_rs.tick(app, info)
+ if action == 0:
+ command = "FORWARD"
+ elif action == 1:
+ command = "BACKWARD"
+ elif action == 2:
+ command = "TURN_RIGHT"
+ elif action == 3:
+ command = "TURN_LEFT"
+ elif action == 4:
+ command = "AIM_RIGHT"
+ elif action == 5:
+ command = "AIM_LEFT"
+ elif action == 6:
+ command = "SHOOT"
+ elif action == 7:
+ command = "NONE"
+
+ return [command]
+
+ def reset(self):
+ """
+ Reset the status
+ """
+ print(f"reset Game {self.side}")
diff --git a/tank-rust/.gitignore b/tank-rust/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/tank-rust/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/tank-rust/Cargo.lock b/tank-rust/Cargo.lock
new file mode 100644
index 0000000..2da1567
--- /dev/null
+++ b/tank-rust/Cargo.lock
@@ -0,0 +1,3574 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+
+[[package]]
+name = "ash"
+version = "0.37.3+1.3.251"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
+dependencies = [
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bincode"
+version = "2.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "burn"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3960b57a6ad4baf54d1dba766965e4559c4b9a8f391107fee5de29db57265840"
+dependencies = [
+ "burn-core",
+ "burn-train",
+]
+
+[[package]]
+name = "burn-autodiff"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9479c28bdce3f2b1541f0a9215628f6256b5f3d66871192a3c56d55171d28e"
+dependencies = [
+ "burn-common",
+ "burn-tensor",
+ "derive-new",
+ "log",
+ "spin",
+]
+
+[[package]]
+name = "burn-candle"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d811c54fa6d9beb38808a1aabd9515c39090720cae572d54f25c041b1702e8fd"
+dependencies = [
+ "burn-tensor",
+ "candle-core",
+ "derive-new",
+ "half",
+]
+
+[[package]]
+name = "burn-common"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9540b2f45a2d337220e702d7a87572c8e1c78db91a200b22924a8c4a6e9be4"
+dependencies = [
+ "async-trait",
+ "derive-new",
+ "getrandom",
+ "rand",
+ "serde",
+ "spin",
+ "uuid",
+ "web-time",
+]
+
+[[package]]
+name = "burn-compute"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e890d8999b25a1a090c2afe198243fc79f0a299efb531a4871c084b0ab9fa11"
+dependencies = [
+ "burn-common",
+ "derive-new",
+ "dirs",
+ "hashbrown 0.14.5",
+ "log",
+ "md5",
+ "serde",
+ "serde_json",
+ "spin",
+ "web-time",
+]
+
+[[package]]
+name = "burn-core"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8af6bc0afe55a57ff0b08f52302df4e3d09f96805a4f1e15c521f1082cb02b4f"
+dependencies = [
+ "bincode 2.0.0-rc.3",
+ "burn-autodiff",
+ "burn-candle",
+ "burn-common",
+ "burn-dataset",
+ "burn-derive",
+ "burn-ndarray",
+ "burn-tch",
+ "burn-tensor",
+ "burn-wgpu",
+ "derive-new",
+ "flate2",
+ "half",
+ "hashbrown 0.14.5",
+ "log",
+ "num-traits",
+ "rand",
+ "rmp-serde",
+ "serde",
+ "serde_json",
+ "spin",
+]
+
+[[package]]
+name = "burn-dataset"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3feae7766b56e947d38ac4d6903388270d848609339a147a513145703426f6db"
+dependencies = [
+ "csv",
+ "derive-new",
+ "dirs",
+ "gix-tempfile",
+ "image",
+ "r2d2",
+ "r2d2_sqlite",
+ "rand",
+ "rmp-serde",
+ "rusqlite",
+ "sanitize-filename",
+ "serde",
+ "serde_json",
+ "serde_rusqlite",
+ "strum",
+ "strum_macros",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "burn-derive"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8618ac2c171c7054ffd3ce8da15c3d4b11dc805eb393065c74c05882ef79d931"
+dependencies = [
+ "derive-new",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "burn-fusion"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d77b882d131a67d15f91b915fb3e0a5add73547e7352310d33c877fbe77c79e"
+dependencies = [
+ "burn-common",
+ "burn-tensor",
+ "derive-new",
+ "hashbrown 0.14.5",
+ "log",
+ "serde",
+ "spin",
+]
+
+[[package]]
+name = "burn-jit"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cb62a93030a690c329b95c01b43e3064a4bd36031e9111d537641d36e42f3ac"
+dependencies = [
+ "burn-common",
+ "burn-compute",
+ "burn-fusion",
+ "burn-tensor",
+ "bytemuck",
+ "derive-new",
+ "hashbrown 0.14.5",
+ "log",
+ "num-traits",
+ "rand",
+ "serde",
+ "spin",
+ "text_placeholder",
+]
+
+[[package]]
+name = "burn-ndarray"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f40bb0b5938937a721045752f1ec1baee8a873429fd17e6e6f2155c6cdf33a"
+dependencies = [
+ "burn-autodiff",
+ "burn-common",
+ "burn-tensor",
+ "derive-new",
+ "libm",
+ "matrixmultiply",
+ "ndarray",
+ "num-traits",
+ "rand",
+ "rayon",
+ "spin",
+]
+
+[[package]]
+name = "burn-tch"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb9c2b547499a3d990e93b950965b9a478edfec4a7bf98d5d4412ff8c897129"
+dependencies = [
+ "burn-tensor",
+ "half",
+ "libc",
+ "rand",
+ "tch",
+]
+
+[[package]]
+name = "burn-tensor"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa19c21f54e1a189be3bbaec45efafdf1c89b2763710b381c9f32ae25e7dbe8"
+dependencies = [
+ "burn-common",
+ "derive-new",
+ "half",
+ "hashbrown 0.14.5",
+ "num-traits",
+ "rand",
+ "rand_distr",
+ "serde",
+]
+
+[[package]]
+name = "burn-train"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a0014ee82ef967bd82dda378cfaf340f255c39c729e29ac3bc65d3107e4c7ee"
+dependencies = [
+ "burn-core",
+ "crossterm",
+ "derive-new",
+ "log",
+ "nvml-wrapper",
+ "ratatui",
+ "serde",
+ "sysinfo",
+ "systemstat",
+ "tracing-appender",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "burn-wgpu"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1575890471123109c6aeb725c52ac649fa9e0013e2303f57dc534d5e0cb857e5"
+dependencies = [
+ "burn-common",
+ "burn-compute",
+ "burn-fusion",
+ "burn-jit",
+ "burn-tensor",
+ "bytemuck",
+ "derive-new",
+ "futures-intrusive",
+ "hashbrown 0.14.5",
+ "log",
+ "pollster",
+ "wgpu",
+]
+
+[[package]]
+name = "bytemuck"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytesize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
+
+[[package]]
+name = "bzip2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "candle-core"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f1b20174c1707e20f4cb364a355b449803c03e9b0c9193324623cf9787a4e00"
+dependencies = [
+ "byteorder",
+ "gemm",
+ "half",
+ "memmap2",
+ "num-traits",
+ "num_cpus",
+ "rand",
+ "rand_distr",
+ "rayon",
+ "safetensors 0.4.3",
+ "thiserror",
+ "yoke",
+ "zip",
+]
+
+[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
+name = "cc"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
+dependencies = [
+ "jobserver",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "com"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
+dependencies = [
+ "com_macros",
+]
+
+[[package]]
+name = "com_macros"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
+dependencies = [
+ "com_macros_support",
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "com_macros_support"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
+name = "crossterm"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
+dependencies = [
+ "bitflags 2.5.0",
+ "crossterm_winapi",
+ "libc",
+ "mio",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "csv"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "d3d12"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307"
+dependencies = [
+ "bitflags 2.5.0",
+ "libloading 0.8.3",
+ "winapi",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
+dependencies = [
+ "cfg-if",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive-new"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dirs"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "dyn-stack"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e53799688f5632f364f8fb387488dd05db9fe45db7011be066fc20e7027f8b"
+dependencies = [
+ "bytemuck",
+ "reborrow",
+]
+
+[[package]]
+name = "either"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+
+[[package]]
+name = "enum-as-inner"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "exr"
+version = "1.72.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
+dependencies = [
+ "bit_field",
+ "flume",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "faster-hex"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "flume"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-intrusive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
+dependencies = [
+ "futures-core",
+ "lock_api",
+ "parking_lot",
+]
+
+[[package]]
+name = "gemm"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab24cc62135b40090e31a76a9b2766a501979f3070fa27f689c27ec04377d32"
+dependencies = [
+ "dyn-stack",
+ "gemm-c32",
+ "gemm-c64",
+ "gemm-common",
+ "gemm-f16",
+ "gemm-f32",
+ "gemm-f64",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-c32"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9c030d0b983d1e34a546b86e08f600c11696fde16199f971cd46c12e67512c0"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-c64"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbb5f2e79fefb9693d18e1066a557b4546cd334b226beadc68b11a8f9431852a"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-common"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2e7ea062c987abcd8db95db917b4ffb4ecdfd0668471d8dc54734fdff2354e8"
+dependencies = [
+ "bytemuck",
+ "dyn-stack",
+ "half",
+ "num-complex",
+ "num-traits",
+ "once_cell",
+ "paste",
+ "pulp",
+ "raw-cpuid",
+ "rayon",
+ "seq-macro",
+ "sysctl",
+]
+
+[[package]]
+name = "gemm-f16"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ca4c06b9b11952071d317604acb332e924e817bd891bec8dfb494168c7cedd4"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "gemm-f32",
+ "half",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "rayon",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-f32"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9a69f51aaefbd9cf12d18faf273d3e982d9d711f60775645ed5c8047b4ae113"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "gemm-f64"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa397a48544fadf0b81ec8741e5c0fba0043008113f71f2034def1935645d2b0"
+dependencies = [
+ "dyn-stack",
+ "gemm-common",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "raw-cpuid",
+ "seq-macro",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2"
+dependencies = [
+ "gix-hash",
+ "gix-trace",
+ "libc",
+]
+
+[[package]]
+name = "gix-fs"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107"
+dependencies = [
+ "gix-features",
+]
+
+[[package]]
+name = "gix-hash"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0"
+dependencies = [
+ "faster-hex",
+ "thiserror",
+]
+
+[[package]]
+name = "gix-tempfile"
+version = "11.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388dd29114a86ec69b28d1e26d6d63a662300ecf61ab3f4cc578f7d7dc9e7e23"
+dependencies = [
+ "dashmap",
+ "gix-fs",
+ "libc",
+ "once_cell",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-registry",
+ "tempfile",
+]
+
+[[package]]
+name = "gix-trace"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e"
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glow"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+dependencies = [
+ "bitflags 2.5.0",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884"
+dependencies = [
+ "log",
+ "presser",
+ "thiserror",
+ "winapi",
+ "windows",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
+dependencies = [
+ "bitflags 2.5.0",
+ "gpu-descriptor-types",
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "bytemuck",
+ "cfg-if",
+ "crunchy",
+ "num-traits",
+ "rand",
+ "rand_distr",
+ "serde",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "serde",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
+dependencies = [
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "hassle-rs"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
+dependencies = [
+ "bitflags 2.5.0",
+ "com",
+ "libc",
+ "libloading 0.8.3",
+ "thiserror",
+ "widestring",
+ "winapi",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "exr",
+ "gif",
+ "jpeg-decoder",
+ "num-traits",
+ "png",
+ "qoi",
+ "tiff",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "indoc"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
+dependencies = [
+ "libc",
+ "libloading 0.8.3",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.5.0",
+ "libc",
+]
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "lru"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+dependencies = [
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matrixmultiply"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
+dependencies = [
+ "autocfg",
+ "num_cpus",
+ "once_cell",
+ "rawpointer",
+ "thread-tree",
+]
+
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "memmap2"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
+dependencies = [
+ "libc",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "metal"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
+dependencies = [
+ "bitflags 2.5.0",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
+dependencies = [
+ "adler",
+ "simd-adler32",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "naga"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843"
+dependencies = [
+ "bit-set",
+ "bitflags 2.5.0",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
+name = "ndarray"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "rawpointer",
+ "rayon",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.5.0+25.2.9519653"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "bytemuck",
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "nvml-wrapper"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd21b9f5a1cce3c3515c9ffa85f5c7443e07162dae0ccf4339bb7ca38ad3454"
+dependencies = [
+ "bitflags 1.3.2",
+ "libloading 0.7.4",
+ "nvml-wrapper-sys",
+ "static_assertions",
+ "thiserror",
+ "wrapcenum-derive",
+]
+
+[[package]]
+name = "nvml-wrapper-sys"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c961a2ea9e91c59a69b78e69090f6f5b867bb46c0c56de9482da232437c4987e"
+dependencies = [
+ "libloading 0.7.4",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "password-hash"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pbkdf2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
+dependencies = [
+ "digest",
+ "hmac",
+ "password-hash",
+ "sha2",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "png"
+version = "0.17.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "pollster"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "presser"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
+
+[[package]]
+name = "pulp"
+version = "0.18.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "762ad20f6b65f5a33874de3b7506d6cf6631d37fb9c0c6ee0e310d7210a167d9"
+dependencies = [
+ "bytemuck",
+ "libm",
+ "num-complex",
+ "reborrow",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r2d2"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
+dependencies = [
+ "log",
+ "parking_lot",
+ "scheduled-thread-pool",
+]
+
+[[package]]
+name = "r2d2_sqlite"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dc290b669d30e20751e813517bbe13662d020419c5c8818ff10b6e8bb7777f6"
+dependencies = [
+ "r2d2",
+ "rusqlite",
+ "uuid",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
+
+[[package]]
+name = "ratatui"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb"
+dependencies = [
+ "bitflags 2.5.0",
+ "cassowary",
+ "crossterm",
+ "indoc",
+ "itertools",
+ "lru",
+ "paste",
+ "stability",
+ "strum",
+ "time",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "10.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "reborrow"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "renderdoc-sys"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rmp"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
+dependencies = [
+ "byteorder",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
+dependencies = [
+ "byteorder",
+ "rmp",
+ "serde",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
+dependencies = [
+ "bitflags 2.5.0",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "safetensors"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d93279b86b3de76f820a8854dd06cbc33cfa57a417b19c47f6a25280112fb1df"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "safetensors"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ced76b22c7fba1162f11a5a75d9d8405264b467a07ae0c9c29be119b9297db9"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "sanitize-filename"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "scheduled-thread-pool"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
+dependencies = [
+ "parking_lot",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "seq-macro"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
+
+[[package]]
+name = "serde"
+version = "1.0.202"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.202"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_rusqlite"
+version = "0.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4600dac14aada464c5584d327baa164e372153309bc4c0fb1498bbfbaa5a028b"
+dependencies = [
+ "rusqlite",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spirv"
+version = "0.3.0+sdk-1.3.268.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "stability"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+dependencies = [
+ "strum_macros",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "sysctl"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea"
+dependencies = [
+ "bitflags 2.5.0",
+ "byteorder",
+ "enum-as-inner",
+ "libc",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.30.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae"
+dependencies = [
+ "cfg-if",
+ "core-foundation-sys",
+ "libc",
+ "ntapi",
+ "once_cell",
+ "rayon",
+ "windows",
+]
+
+[[package]]
+name = "systemstat"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a24aec24a9312c83999a28e3ef9db7e2afd5c64bf47725b758cdc1cafd5b0bd2"
+dependencies = [
+ "bytesize",
+ "lazy_static",
+ "libc",
+ "nom",
+ "time",
+ "winapi",
+]
+
+[[package]]
+name = "tank-rust"
+version = "0.1.0"
+dependencies = [
+ "bincode 1.3.3",
+ "burn",
+ "cfg-if",
+ "lazy_static",
+ "rand",
+ "serde",
+]
+
+[[package]]
+name = "tch"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c7cb00bc2770454b515388d45be7097a3ded2eca172f3dcdb7ca4cc06c40bf1"
+dependencies = [
+ "half",
+ "lazy_static",
+ "libc",
+ "ndarray",
+ "rand",
+ "safetensors 0.3.3",
+ "thiserror",
+ "torch-sys",
+ "zip",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "text_placeholder"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512104f982ce6f50def5340f9d7d14cc21f7a859e9ccd251aa19d12e1345c070"
+dependencies = [
+ "hashbrown 0.13.2",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "thread-tree"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbd370cb847953a25954d9f63e14824a36113f8c72eecf6eccef5dc4b45d630"
+dependencies = [
+ "crossbeam-channel",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "torch-sys"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29e0244e5b148a31dd7fe961165037d1927754d024095c1013937532d7e73a22"
+dependencies = [
+ "anyhow",
+ "cc",
+ "libc",
+ "serde",
+ "serde_json",
+ "ureq",
+ "zip",
+]
+
+[[package]]
+name = "tracing-appender"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
+dependencies = [
+ "crossbeam-channel",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "ureq"
+version = "2.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
+dependencies = [
+ "base64",
+ "flate2",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "serde",
+ "serde_json",
+ "url",
+ "webpki-roots",
+]
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "uuid"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+dependencies = [
+ "getrandom",
+ "rand",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
+[[package]]
+name = "wgpu"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01"
+dependencies = [
+ "arrayvec",
+ "cfg-if",
+ "cfg_aliases",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags 2.5.0",
+ "cfg_aliases",
+ "codespan-reporting",
+ "indexmap",
+ "log",
+ "naga",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1a4924366df7ab41a5d8546d6534f1f33231aa5b3f72b9930e300f254e39c3"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags 2.5.0",
+ "block",
+ "cfg_aliases",
+ "core-graphics-types",
+ "d3d12",
+ "glow",
+ "glutin_wgl_sys",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "hassle-rs",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading 0.8.3",
+ "log",
+ "metal",
+ "naga",
+ "ndk-sys",
+ "objc",
+ "once_cell",
+ "parking_lot",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805"
+dependencies = [
+ "bitflags 2.5.0",
+ "js-sys",
+ "web-sys",
+]
+
+[[package]]
+name = "widestring"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
+[[package]]
+name = "wrapcenum-derive"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
+
+[[package]]
+name = "yoke"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.65",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
+[[package]]
+name = "zip"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
+dependencies = [
+ "aes",
+ "byteorder",
+ "bzip2",
+ "constant_time_eq",
+ "crc32fast",
+ "crossbeam-utils",
+ "flate2",
+ "hmac",
+ "pbkdf2",
+ "sha1",
+ "time",
+ "zstd",
+]
+
+[[package]]
+name = "zstd"
+version = "0.11.2+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "5.0.2+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.10+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
diff --git a/tank-rust/Cargo.toml b/tank-rust/Cargo.toml
new file mode 100644
index 0000000..9cc34db
--- /dev/null
+++ b/tank-rust/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "tank-rust"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[profile.release]
+lto = true
+opt-level = 's'
+panic = "abort"
+strip = "symbols"
+
+[lib]
+crate_type = ["cdylib"]
+
+[dependencies]
+burn = { version = "0.13.2", features = ["wgpu","dataset","train","tui","metrics","autodiff"] }
+serde = {version="1.0.202", features = ["derive"]}
+bincode = "1.3.3"
+rand = "0.8.5"
+lazy_static = "1.4.0"
+cfg-if = "1.0.0"
+
+[features]
+default = ["train"]
+train = []
diff --git a/tank-rust/src/dqn/collect.rs b/tank-rust/src/dqn/collect.rs
new file mode 100644
index 0000000..27a3455
--- /dev/null
+++ b/tank-rust/src/dqn/collect.rs
@@ -0,0 +1,90 @@
+use std::{
+ fs::{File, OpenOptions},
+ io::{BufWriter, Write},
+};
+
+use super::dataset::TankItem;
+use crate::{ffi::prelude::*, Backend};
+use burn::{backend::wgpu::WgpuDevice, module::Module, record::NoStdTrainingRecorder};
+use rand::{thread_rng, Rng};
+
+use super::model::{DQNModel, DQNModelConfig};
+const EXPLORE_RATE: f32 = 0.4;
+
+pub struct App<'a> {
+ model: DQNModel,
+ device: WgpuDevice,
+ last_state_action: Option<(Info<'a>, Action)>,
+ #[cfg(feature = "train")]
+ outlet: BufWriter,
+}
+
+impl<'a> App<'a> {
+ pub fn new(model_path: &str) -> Self {
+ let device = burn::backend::wgpu::WgpuDevice::default();
+
+ let model = DQNModelConfig::new().init(&device);
+
+ let model = model
+ .load_file(
+ format!("{model_path}/model"),
+ &NoStdTrainingRecorder::new(),
+ &device,
+ )
+ .unwrap();
+ Self {
+ model,
+ device,
+ last_state_action: None,
+ #[cfg(feature = "train")]
+ outlet: BufWriter::new(
+ OpenOptions::new()
+ .write(true)
+ .create(true)
+ .open(format!("{model_path}/dataset"))
+ .unwrap(),
+ ),
+ }
+ }
+ #[cfg(feature = "train")]
+ pub fn flush(&mut self) {
+ self.outlet.flush().unwrap();
+ }
+ #[cfg(feature = "train")]
+ pub fn collect_data(&mut self, state: &Info<'static>) -> Action {
+ if let Some((previous_state, action)) = self.last_state_action.take() {
+ let reward = previous_state.get_reward(state, action);
+ let item = TankItem {
+ previous_state: previous_state.into_feature(),
+ new_state: state.into_feature(),
+ action,
+ reward,
+ };
+ bincode::serialize_into(&mut self.outlet, &item).unwrap();
+ }
+
+ let action = match thread_rng().gen_ratio((4096.0 * EXPLORE_RATE) as u32, 4096) {
+ true => match thread_rng().gen_range(0..15 as i32) {
+ 0 => Action::Forward,
+ 1 => Action::Backward,
+ 2 => Action::TurnRight,
+ 3 => Action::TurnLeft,
+ 4 => Action::AimRight,
+ 5 => Action::AimLeft,
+ 6 => Action::Shoot,
+ 7 => Action::TurnRight,
+ _ => Action::Forward,
+ },
+ false => self.predict_action(state),
+ };
+
+ self.last_state_action = Some((state.clone(), action));
+
+ action
+ }
+ pub fn predict_action(&self, state: &Info) -> Action {
+ let input = state.into_feature_tensor(&self.device).unsqueeze(); // Convert input tensor to shape [1, input_size]
+ let ans = self.model.forward(input);
+ ans.argmax(1).into_scalar().try_into().unwrap()
+ }
+}
diff --git a/tank-rust/src/dqn/dataset.rs b/tank-rust/src/dqn/dataset.rs
new file mode 100644
index 0000000..db61d2d
--- /dev/null
+++ b/tank-rust/src/dqn/dataset.rs
@@ -0,0 +1,127 @@
+use std::{
+ env::{self},
+ fs::File,
+ io::BufReader,
+};
+
+use crate::ffi::prelude::*;
+use crate::ARTIFACT_DIR;
+use burn::{
+ data::{dataloader::batcher::Batcher, dataset::Dataset},
+ prelude::*,
+};
+
+use super::feature::FEATRUE_SPACE;
+
+#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
+pub struct TankItem {
+ pub previous_state: [f32; FEATRUE_SPACE],
+ pub new_state: [f32; FEATRUE_SPACE],
+ pub action: Action,
+ pub reward: f32,
+}
+
+pub struct TankDataset {
+ pub dataset: Vec,
+}
+
+impl Dataset for TankDataset {
+ fn get(&self, index: usize) -> Option {
+ self.dataset.get(index).cloned()
+ }
+
+ fn len(&self) -> usize {
+ self.dataset.len()
+ }
+}
+
+impl TankDataset {
+ pub fn new() -> Self {
+ Self {
+ dataset: Vec::new(),
+ }
+ }
+ pub fn load() -> Self {
+ let dataset_path = env::var("MODEL_PATH").unwrap_or_else(|_| ARTIFACT_DIR.to_string());
+ let mut dataset = Vec::new();
+ println!("Loading dataset from: {}", dataset_path);
+ if let Ok(reader) = File::open(format!("{dataset_path}/dataset")) {
+ let mut reader = BufReader::new(reader);
+ while let Ok(item) = bincode::deserialize_from::<_, TankItem>(&mut reader) {
+ dataset.push(item);
+ }
+ }
+ TankDataset { dataset }
+ }
+ pub fn add(&mut self, item: TankItem) {
+ self.dataset.push(item);
+ }
+ pub fn split(self, ratio: f32) -> (Self, Self) {
+ let split = (self.dataset.len() as f32 * ratio).round() as usize;
+ let (a, b) = self.dataset.split_at(split);
+ (
+ TankDataset {
+ dataset: a.to_vec(),
+ },
+ TankDataset {
+ dataset: b.to_vec(),
+ },
+ )
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct TankBatcher {
+ device: B::Device,
+}
+
+#[derive(Clone, Debug)]
+pub struct TankBatch {
+ pub new_state: Tensor,
+ pub old_state: Tensor,
+ pub action: Tensor,
+ pub reward: Tensor,
+}
+
+impl TankBatcher {
+ pub fn new(device: B::Device) -> Self {
+ Self { device }
+ }
+}
+
+impl Batcher> for TankBatcher {
+ fn batch(&self, items: Vec) -> TankBatch {
+ let mut new_state: Vec> = Vec::new();
+ let mut old_state: Vec> = Vec::new();
+
+ for item in items.iter() {
+ let new_state_tensor = Tensor::::from_floats(item.previous_state, &self.device);
+ let old_state_tensor = Tensor::::from_floats(item.new_state, &self.device);
+
+ new_state.push(new_state_tensor.unsqueeze());
+ old_state.push(old_state_tensor.unsqueeze());
+ }
+
+ let new_state = Tensor::cat(new_state, 0);
+ let old_state = Tensor::cat(old_state, 0);
+
+ let reward = items
+ .iter()
+ .map(|item| Tensor::::from_floats([item.reward], &self.device))
+ .collect();
+ let reward = Tensor::cat(reward, 0);
+
+ let response = items
+ .iter()
+ .map(|item| Tensor::::from_ints([item.action as i32], &self.device))
+ .collect();
+ let response = Tensor::cat(response, 0);
+
+ TankBatch {
+ new_state,
+ old_state,
+ reward,
+ action: response,
+ }
+ }
+}
diff --git a/tank-rust/src/dqn/feature.rs b/tank-rust/src/dqn/feature.rs
new file mode 100644
index 0000000..30dff81
--- /dev/null
+++ b/tank-rust/src/dqn/feature.rs
@@ -0,0 +1,192 @@
+//! Feature extraction and reward calculation for DQN
+use std::f32::consts::PI;
+
+use burn::tensor::{backend::Backend, Tensor};
+
+use crate::ffi::prelude::*;
+
+pub const FEATRUE_SPACE: usize = 10;
+pub const ACTION_SPACE: usize = 7;
+
+#[derive(PartialEq, Default)]
+struct Polar {
+ angle: f32,
+ distance: f32,
+}
+
+impl Polar {
+ pub fn clip(&self) -> Self {
+ Polar {
+ angle: self.angle,
+ distance: self.distance.min(1e6).max(0.0),
+ }
+ }
+}
+
+impl Eq for Polar {}
+
+impl Ord for Polar {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.distance.partial_cmp(&other.distance).unwrap()
+ }
+}
+
+impl PartialOrd for Polar {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ self.distance.partial_cmp(&other.distance)
+ }
+}
+
+fn normalize_angle(mut angle: f32) -> f32 {
+ while angle < -PI {
+ angle += 2.0 * PI;
+ }
+ while angle >= PI {
+ angle -= 2.0 * PI;
+ }
+ angle
+}
+
+impl Player {
+ fn to_pos(&self) -> (i32, i32) {
+ (self.x, self.y)
+ }
+ fn center(&self, x: i32, y: i32) -> Polar {
+ let dx = x - self.x;
+ let dy = y - self.y;
+ let angle = (dy as f32).atan2(dx as f32);
+ let distance = (dx.pow(2) + dy.pow(2)) as f32;
+ Polar { angle, distance }
+ }
+ fn closest(&self, others: impl Iterator- ) -> Polar {
+ others
+ .map(|(x, y)| self.center(x, y))
+ .min()
+ .unwrap_or_default()
+ }
+ fn get_angle(&self) -> f32 {
+ (180 - self.angle) as f32 / 360.0 * 2.0 * PI
+ }
+ fn get_gun_angle(&self) -> f32 {
+ self.gun_angle as f32 / 360.0 * 2.0 * PI
+ }
+}
+
+#[derive(Debug)]
+enum Target {
+ Oil,
+ Bullet,
+ Enemy,
+}
+
+impl Target {
+ fn get_pos(&self, info: &Info) -> Polar {
+ match self {
+ Target::Oil => info
+ .player
+ .closest(info.oil_stations.iter().map(Station::to_pos)),
+ Target::Bullet => info
+ .player
+ .closest(info.bullet_stations.iter().map(Station::to_pos)),
+ Target::Enemy => info.player.closest(info.enemies.iter().map(Player::to_pos)),
+ }
+ }
+ fn reach(&self, last: &Info, current: &Info) -> bool {
+ match self {
+ Target::Oil => last.player.oil > current.player.oil,
+ Target::Bullet => last.player.power > current.player.power,
+ Target::Enemy => false,
+ }
+ }
+}
+
+impl Station {
+ fn to_pos(&self) -> (i32, i32) {
+ (self.x as i32, self.y as i32)
+ }
+}
+
+impl Wall {
+ fn to_pos(&self) -> (i32, i32) {
+ (self.x, self.y)
+ }
+}
+
+impl<'a> Info<'a> {
+ pub fn into_feature(&self) -> [f32; FEATRUE_SPACE] {
+ let emeny = self.player.closest(self.enemies.iter().map(Player::to_pos));
+ let wall = self
+ .player
+ .closest(self.walls.iter().map(|wall| (wall.x, wall.y)));
+ let bullet = self
+ .player
+ .closest(self.bullets.iter().map(|bullet| (bullet.x, bullet.y)));
+
+ let target = self.get_target().get_pos(self).clip();
+
+ let angle = self.player.get_angle();
+ let gun_angle = self.player.get_gun_angle();
+
+ let feature = [
+ normalize_angle(target.angle - angle).tanh(),
+ normalize_angle(target.angle - angle + PI).tanh(),
+ normalize_angle(bullet.angle - angle).tanh(),
+ (target.distance + 1.0).log2(),
+ (wall.distance - target.distance).tanh(),
+ (bullet.distance + 1.0).log2(),
+ normalize_angle(emeny.angle - gun_angle).tanh(),
+ normalize_angle(wall.angle - gun_angle).tanh(),
+ (self.player.oil - 40.0).tanh(),
+ (self.player.power as f32 - 7.0).tanh(),
+ ];
+
+ feature
+ }
+ pub fn into_feature_tensor(&self, device: &B::Device) -> Tensor {
+ let feature = self.into_feature();
+ Tensor::from_floats(feature, device)
+ }
+ fn get_target(&self) -> Target {
+ if self.player.oil < 40.0 {
+ Target::Oil
+ } else if self.player.power > 7 {
+ Target::Enemy
+ } else {
+ Target::Bullet
+ }
+ }
+ pub fn get_reward(&self, next: &Self, action: Action) -> f32 {
+ let same_position = self.player.x == next.player.x && self.player.y == next.player.y;
+ let mut reward = -2.3;
+ reward += match action {
+ Action::Forward | Action::Backward if same_position => -8.0,
+ Action::Shoot => match next.player.power > 7 {
+ true => 2.0,
+ false => -2.0,
+ },
+ _ => 0.0,
+ };
+
+ let target = self.get_target();
+
+ if target.reach(self, next) {
+ reward += 15.0;
+ } else {
+ let previous_target_position = target.get_pos(self);
+ let next_target_position = target.get_pos(next);
+
+ reward += match previous_target_position.cmp(&next_target_position) {
+ std::cmp::Ordering::Less => -5.0,
+ std::cmp::Ordering::Equal => 0.0,
+ std::cmp::Ordering::Greater => 5.8,
+ };
+ }
+
+ reward
+ + match next.player.score - self.player.score {
+ x if x > 2 => 20.0,
+ x if x > 0 => 10.0, // too high, tank my ignore power station
+ _ => -1.0,
+ }
+ }
+}
diff --git a/tank-rust/src/dqn/mod.rs b/tank-rust/src/dqn/mod.rs
new file mode 100644
index 0000000..3050f00
--- /dev/null
+++ b/tank-rust/src/dqn/mod.rs
@@ -0,0 +1,12 @@
+mod collect;
+mod dataset;
+mod feature;
+mod model;
+mod training;
+
+pub mod prelude {
+ pub use super::collect::App as DQNApp;
+ pub use super::dataset::{TankDataset, TankItem};
+ pub use super::feature::{ACTION_SPACE, FEATRUE_SPACE};
+ pub use super::training::run as train;
+}
diff --git a/tank-rust/src/dqn/model.rs b/tank-rust/src/dqn/model.rs
new file mode 100644
index 0000000..8b0e46c
--- /dev/null
+++ b/tank-rust/src/dqn/model.rs
@@ -0,0 +1,122 @@
+use super::{
+ dataset::TankBatch,
+ feature::{ACTION_SPACE, FEATRUE_SPACE},
+};
+use burn::{
+ nn::{
+ loss::{HuberLoss, HuberLossConfig, Reduction::Mean},
+ Linear, LinearConfig, Relu,
+ },
+ prelude::*,
+ tensor::backend::AutodiffBackend,
+ train::{
+ metric::{Adaptor, LossInput},
+ TrainOutput, TrainStep, ValidStep,
+ },
+};
+
+pub struct DQNOutput {
+ estimated_reward: Tensor,
+ expected_reward: Tensor,
+ loss: Tensor,
+}
+
+impl Adaptor> for DQNOutput {
+ fn adapt(&self) -> LossInput {
+ LossInput::new(self.loss.clone())
+ }
+}
+
+#[derive(Module, Debug)]
+pub struct DQNModel {
+ input_layer: Linear,
+ hidden_layer_1: Linear,
+ hidden_layer_2: Linear,
+ output_layer: Linear,
+ activation: Relu,
+ loss_function: HuberLoss,
+ gamma: f32,
+}
+
+#[derive(Config)]
+pub struct DQNModelConfig {
+ #[config(default = 64)]
+ pub hidden_layer_1_size: usize,
+ #[config(default = 96)]
+ pub hidden_layer_2_size: usize,
+ #[config(default = 64)]
+ pub hidden_layer_3_size: usize,
+ #[config(default = 0.99)]
+ pub gamma: f32,
+}
+
+impl DQNModelConfig {
+ pub fn init(&self, device: &B::Device) -> DQNModel {
+ let input_layer = LinearConfig::new(FEATRUE_SPACE, self.hidden_layer_1_size)
+ .with_bias(true)
+ .init(device);
+ let hidden_layer_1 = LinearConfig::new(self.hidden_layer_1_size, self.hidden_layer_2_size)
+ .with_bias(true)
+ .init(device);
+ let hidden_layer_2 = LinearConfig::new(self.hidden_layer_2_size, self.hidden_layer_3_size)
+ .with_bias(true)
+ .init(device);
+ let output_layer = LinearConfig::new(self.hidden_layer_3_size, ACTION_SPACE)
+ .with_bias(true)
+ .init(device);
+
+ DQNModel {
+ input_layer,
+ hidden_layer_1,
+ hidden_layer_2,
+ output_layer,
+ loss_function: HuberLossConfig::new(1.34).init(device),
+ activation: Relu::new(),
+ gamma: self.gamma,
+ }
+ }
+}
+
+impl DQNModel {
+ pub fn forward(&self, input: Tensor) -> Tensor {
+ let x = input.detach();
+ let x = self.input_layer.forward(x);
+ let x = self.activation.forward(x);
+ self.output_layer.forward(x)
+ }
+
+ pub fn forward_step(&self, item: TankBatch) -> DQNOutput {
+ let estimated_reward = self.forward(item.new_state);
+
+ // FIXME: magic unsqueeze
+ let a = item.action.unsqueeze_dim(1);
+ let x = estimated_reward.clone().gather(1, a);
+
+ // FIXME: what's final mask
+ let expected_reward = self.forward(item.old_state);
+ let y = expected_reward.clone().max_dim(1);
+ let y = y.mul_scalar(self.gamma).add(item.reward.unsqueeze());
+
+ let loss = self.loss_function.forward(x, y, Mean);
+
+ DQNOutput {
+ estimated_reward,
+ expected_reward,
+ loss,
+ }
+ }
+}
+
+impl TrainStep, DQNOutput> for DQNModel {
+ fn step(&self, item: TankBatch) -> TrainOutput> {
+ let item = self.forward_step(item);
+
+ TrainOutput::new(self, item.loss.backward(), item)
+ }
+}
+
+impl ValidStep, DQNOutput> for DQNModel {
+ fn step(&self, item: TankBatch) -> DQNOutput {
+ self.forward_step(item)
+ }
+}
diff --git a/tank-rust/src/dqn/training.rs b/tank-rust/src/dqn/training.rs
new file mode 100644
index 0000000..906b07c
--- /dev/null
+++ b/tank-rust/src/dqn/training.rs
@@ -0,0 +1,103 @@
+use std::{env, fs};
+
+use super::dataset::{TankBatcher, TankDataset};
+use super::model::DQNModelConfig;
+use crate::ARTIFACT_DIR;
+use burn::optim::AdamConfig;
+use burn::train::metric::store::{Aggregate, Direction, Split};
+use burn::train::{MetricEarlyStoppingStrategy, StoppingCondition};
+use burn::{
+ data::{dataloader::DataLoaderBuilder, dataset::Dataset},
+ prelude::*,
+ record::{CompactRecorder, NoStdTrainingRecorder},
+ tensor::backend::AutodiffBackend,
+ train::{metric::LossMetric, LearnerBuilder},
+};
+#[derive(Config)]
+pub struct ExpConfig {
+ #[config(default = 16)]
+ pub num_epochs: usize,
+
+ #[config(default = 6)]
+ pub num_workers: usize,
+
+ #[config(default = 47)]
+ pub seed: u64,
+
+ pub optimizer: AdamConfig,
+
+ #[config(default = 1.5e-3)]
+ pub learn_rate: f64,
+
+ #[config(default = 4096)]
+ pub batch_size: usize,
+}
+
+pub fn run(device: B::Device)
+where
+ B: AutodiffBackend,
+{
+ let model_path = env::var("MODEL_PATH").unwrap_or_else(|_| ARTIFACT_DIR.to_string());
+
+ let optimizer = AdamConfig::new();
+ let config = ExpConfig::new(optimizer);
+ let mut model = DQNModelConfig::new().init(&device);
+
+ if fs::metadata(format!("{model_path}/model")).is_ok() {
+ model = model
+ .load_file(
+ format!("{model_path}/model"),
+ &NoStdTrainingRecorder::new(),
+ &device,
+ )
+ .unwrap();
+ }
+
+ B::seed(config.seed);
+
+ let (train_dataset, test_dataset) = TankDataset::load().split(1.0);
+
+ println!("Train Dataset Size: {}", train_dataset.len());
+ println!("Test Dataset Size: {}", test_dataset.len());
+
+ let batcher_train = TankBatcher::::new(device.clone());
+
+ let batcher_test = TankBatcher::::new(device.clone());
+
+ let dataloader_train = DataLoaderBuilder::new(batcher_train)
+ .batch_size(config.batch_size)
+ .shuffle(config.seed)
+ .num_workers(config.num_workers)
+ .build(train_dataset);
+
+ let dataloader_test = DataLoaderBuilder::new(batcher_test)
+ .batch_size(config.batch_size)
+ .shuffle(config.seed)
+ .num_workers(config.num_workers)
+ .build(test_dataset);
+
+ let learner = LearnerBuilder::new(&model_path)
+ .metric_train_numeric(LossMetric::new())
+ .metric_valid_numeric(LossMetric::new())
+ .with_file_checkpointer(CompactRecorder::new())
+ .early_stopping(MetricEarlyStoppingStrategy::new::>(
+ Aggregate::Mean,
+ Direction::Lowest,
+ Split::Train,
+ StoppingCondition::NoImprovementSince { n_epochs: 2 },
+ ))
+ .devices(vec![device.clone()])
+ .num_epochs(config.num_epochs)
+ .summary()
+ .build(model, config.optimizer.init(), config.learn_rate);
+
+ let model_trained = learner.fit(dataloader_train, dataloader_test);
+
+ config
+ .save(format!("{model_path}/config.json").as_str())
+ .unwrap();
+
+ model_trained
+ .save_file(format!("{model_path}/model"), &NoStdTrainingRecorder::new())
+ .expect("Failed to save trained model");
+}
diff --git a/tank-rust/src/ffi/action.rs b/tank-rust/src/ffi/action.rs
new file mode 100644
index 0000000..045d331
--- /dev/null
+++ b/tank-rust/src/ffi/action.rs
@@ -0,0 +1,30 @@
+#[repr(i32)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
+pub enum Action {
+ Forward = 0,
+ Backward = 1,
+ TurnRight = 2,
+ TurnLeft = 3,
+ AimRight = 4,
+ AimLeft = 5,
+ Shoot = 6,
+ None = 7,
+}
+
+impl TryFrom for Action {
+ type Error = &'static str;
+
+ fn try_from(value: i32) -> Result {
+ match value {
+ 0 => Ok(Action::Forward),
+ 1 => Ok(Action::Backward),
+ 2 => Ok(Action::TurnRight),
+ 3 => Ok(Action::TurnLeft),
+ 4 => Ok(Action::AimRight),
+ 5 => Ok(Action::AimLeft),
+ 6 => Ok(Action::Shoot),
+ 7 => Ok(Action::None),
+ _ => Err("Invalid action"),
+ }
+ }
+}
diff --git a/tank-rust/src/ffi/info/bullet.rs b/tank-rust/src/ffi/info/bullet.rs
new file mode 100644
index 0000000..8917022
--- /dev/null
+++ b/tank-rust/src/ffi/info/bullet.rs
@@ -0,0 +1,6 @@
+#[repr(C)]
+#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
+pub struct Bullet {
+ pub x: i32,
+ pub y: i32,
+}
diff --git a/tank-rust/src/ffi/info/mod.rs b/tank-rust/src/ffi/info/mod.rs
new file mode 100644
index 0000000..10259a1
--- /dev/null
+++ b/tank-rust/src/ffi/info/mod.rs
@@ -0,0 +1,58 @@
+mod bullet;
+mod player;
+mod station;
+mod wall;
+
+pub use bullet::*;
+pub use player::*;
+pub use station::*;
+pub use wall::*;
+
+#[derive(Debug, Clone, Default)]
+pub struct Info<'a> {
+ pub player: Player,
+ pub teammates: &'a [Player],
+ pub enemies: &'a [Player],
+ pub bullets: &'a [Bullet],
+ pub bullet_stations: &'a [Station],
+ pub oil_stations: &'a [Station],
+ pub walls: &'a [Wall],
+}
+
+#[repr(C)]
+pub struct RawInfo {
+ player: *const Player,
+ teammates: *const Player,
+ teammates_len: u32,
+ enemies: *const Player,
+ enemies_len: u32,
+ bullets: *const Bullet,
+ bullet_len: u32,
+ bullet_stations: *const Station,
+ bullet_stations_len: u32,
+ oil_stations: *const Station,
+ oil_stations_len: u32,
+ walls: *const Wall,
+ walls_len: u32,
+}
+
+impl<'a> Info<'a> {
+ pub unsafe fn from_raw(self_: *const RawInfo) -> Self {
+ let raw = &*self_;
+ Info {
+ player: (&*raw.player).clone(),
+ teammates: std::slice::from_raw_parts(raw.teammates, raw.teammates_len as usize),
+ enemies: std::slice::from_raw_parts(raw.enemies, raw.enemies_len as usize),
+ bullets: std::slice::from_raw_parts(raw.bullets, raw.bullet_len as usize),
+ bullet_stations: std::slice::from_raw_parts(
+ raw.bullet_stations,
+ raw.bullet_stations_len as usize,
+ ),
+ oil_stations: std::slice::from_raw_parts(
+ raw.oil_stations,
+ raw.oil_stations_len as usize,
+ ),
+ walls: std::slice::from_raw_parts(raw.walls, raw.walls_len as usize),
+ }
+ }
+}
diff --git a/tank-rust/src/ffi/info/player.rs b/tank-rust/src/ffi/info/player.rs
new file mode 100644
index 0000000..d0c49e6
--- /dev/null
+++ b/tank-rust/src/ffi/info/player.rs
@@ -0,0 +1,14 @@
+#[repr(C)]
+#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, Default)]
+pub struct Player {
+ pub x: i32,
+ pub y: i32,
+ pub speed: i32,
+ pub score: i32,
+ pub power: i32,
+ pub oil: f32,
+ pub lives: i32,
+ pub angle: i32,
+ pub gun_angle: i32,
+ pub cooldown: i32,
+}
diff --git a/tank-rust/src/ffi/info/station.rs b/tank-rust/src/ffi/info/station.rs
new file mode 100644
index 0000000..6abe3f6
--- /dev/null
+++ b/tank-rust/src/ffi/info/station.rs
@@ -0,0 +1,7 @@
+#[repr(C)]
+#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Default)]
+pub struct Station {
+ pub x: i32,
+ pub y: i32,
+ pub power: i32,
+}
diff --git a/tank-rust/src/ffi/info/wall.rs b/tank-rust/src/ffi/info/wall.rs
new file mode 100644
index 0000000..d8c0931
--- /dev/null
+++ b/tank-rust/src/ffi/info/wall.rs
@@ -0,0 +1,7 @@
+#[repr(C)]
+#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
+pub struct Wall {
+ pub x: i32,
+ pub y: i32,
+ pub lives: i32,
+}
diff --git a/tank-rust/src/ffi/mod.rs b/tank-rust/src/ffi/mod.rs
new file mode 100644
index 0000000..25a52af
--- /dev/null
+++ b/tank-rust/src/ffi/mod.rs
@@ -0,0 +1,7 @@
+mod action;
+mod info;
+
+pub mod prelude {
+ pub use super::action::*;
+ pub use super::info::*;
+}
diff --git a/tank-rust/src/fit.rs b/tank-rust/src/fit.rs
new file mode 100644
index 0000000..404c6da
--- /dev/null
+++ b/tank-rust/src/fit.rs
@@ -0,0 +1,77 @@
+use burn::data::dataset::Dataset;
+
+use crate::dqn::prelude::TankItem;
+use crate::ffi::prelude::*;
+use rand::Rng;
+
+// fn random_action() -> Action {
+// let mut rng = rand::thread_rng();
+// match rng.gen_range(0..2) {
+// 0 => Action::AimLeft,
+// 1 => Action::Forward,
+// _ => unreachable!(),
+// }
+// }
+
+// fn random_item() -> TankItem {
+// let mut previous_info=Info::default();
+// TankItem {
+// previous_state: todo!(),
+// new_state: todo!(),
+// action: todo!(),
+// reward: todo!(),
+// }
+// }
+
+pub struct FitDataset;
+
+impl FitDataset {
+ /// Get closer to the power station
+ fn close_power_station() -> TankItem {
+ let mut power_stations = Station::default();
+
+ let mut previous_info = Info::default();
+ let mut new_info = Info::default();
+ let mut rng = rand::thread_rng();
+ previous_info.player.power = rng.gen_range(0..2);
+ new_info.player.power = previous_info.player.power;
+ previous_info.player.angle = rng.gen_range(0..360);
+ new_info.player.angle = previous_info.player.angle;
+
+ TankItem {
+ previous_state: todo!(),
+ new_state: todo!(),
+ action: Action::Forward,
+ reward: todo!(),
+ }
+ }
+ /// Flee from power station if power is high
+ fn flee_power_station() -> TankItem {
+ let mut previous_info = Info::default();
+ TankItem {
+ previous_state: todo!(),
+ new_state: todo!(),
+ action: Action::Backward,
+ reward: todo!(),
+ }
+ }
+}
+
+impl Dataset for FitDataset {
+ fn get(&self, _: usize) -> Option {
+ let previous_state = todo!();
+ let new_state = todo!();
+ let action = Action::AimLeft;
+ let reward = 0.0;
+ Some(TankItem {
+ previous_state,
+ new_state,
+ action,
+ reward,
+ })
+ }
+
+ fn len(&self) -> usize {
+ 1
+ }
+}
diff --git a/tank-rust/src/lib.rs b/tank-rust/src/lib.rs
new file mode 100644
index 0000000..4fe8d71
--- /dev/null
+++ b/tank-rust/src/lib.rs
@@ -0,0 +1,44 @@
+mod dqn;
+mod ffi;
+use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
+
+use burn::backend::{wgpu::AutoGraphicsApi, Wgpu};
+use dqn::prelude::*;
+use ffi::prelude::*;
+
+static ARTIFACT_DIR: &str = "../output";
+
+type Backend = Wgpu;
+
+#[no_mangle]
+pub extern "C" fn init(model_path: *const u8, len: i32) -> *mut DQNApp<'static> {
+ let model_path =
+ OsStr::from_bytes(unsafe { std::slice::from_raw_parts(model_path, len as usize) })
+ .to_str()
+ .unwrap();
+ let app = DQNApp::new(model_path);
+
+ Box::into_raw(Box::new(app))
+}
+
+#[no_mangle]
+pub extern "C" fn tick(app: *mut DQNApp, raw: *mut RawInfo) -> i32 {
+ let app = unsafe { &mut *app };
+ let info: Info<'static> = unsafe { Info::from_raw(raw) };
+ cfg_if::cfg_if! {
+ if #[cfg(feature = "train")]{
+ app.collect_data(&info)as i32
+ }else{
+ app.predict_action(&info)as i32
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn flush(app: *mut DQNApp) {
+ let app = unsafe { &mut *app };
+ #[cfg(feature = "train")]
+ {
+ app.flush();
+ }
+}
diff --git a/tank-rust/src/main.rs b/tank-rust/src/main.rs
new file mode 100644
index 0000000..592bd8f
--- /dev/null
+++ b/tank-rust/src/main.rs
@@ -0,0 +1,14 @@
+mod dqn;
+mod ffi;
+use burn::backend::{wgpu::AutoGraphicsApi, Autodiff, Wgpu};
+
+static ARTIFACT_DIR: &str = "../output";
+
+type Backend = Wgpu;
+type AutodiffBackend = Autodiff;
+
+pub fn main() {
+ let device = burn::backend::wgpu::WgpuDevice::default();
+
+ dqn::prelude::train::(device);
+}
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..7a0a0f7
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6
+if [ "$XDG_SESSION_TYPE" == "x11" ]; then
+ export DISPLAY=:0.0
+fi
+cd TankMan
+python -m mlgame -f 400 \
+-i ../ml/collect1.py -i ../ml/test.py \
+. --green_team_num 1 --blue_team_num 1 --is_manual 1 \
+--frame_limit 1000 > /dev/null
+cd ..
diff --git a/train.sh b/train.sh
new file mode 100755
index 0000000..9d75305
--- /dev/null
+++ b/train.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6
+if [ "$XDG_SESSION_TYPE" == "x11" ]; then
+ export DISPLAY=:0.0
+fi
+
+for i in $(seq 1 $1);
+do
+ echo "epoch $i"
+ cd TankMan
+ timeout 240 python -m mlgame -f 3000 \
+ -i ../ml/collect1.py -i ../ml/collect2.py \
+ . --green_team_num 1 --blue_team_num 1 --is_manual 1 \
+ --frame_limit 1000 > /dev/null
+ cd ..
+
+ MODEL_PATH="output1" ./train
+ MODEL_PATH="output2" ./train
+
+ rm output1/dataset
+ rm output2/dataset
+done