Compare commits

..

24 Commits

Author SHA1 Message Date
Bel LaPointe
02b74c8ee5 doesnt work 2025-03-31 19:08:38 -06:00
Bel LaPointe
42831b71a1 update godot naming 2025-03-31 18:51:27 -06:00
Bel LaPointe
8e3cecdc6a gmon aliases mon 2025-03-31 18:46:42 -06:00
Bel LaPointe
e94a802034 gmon aliases mon 2025-03-31 18:46:28 -06:00
Bel LaPointe
9130797eec rust to gmon imports purerust to mon 2025-03-31 18:39:25 -06:00
Bel LaPointe
61f7299e42 comments 2025-03-24 21:10:58 -06:00
Bel LaPointe
5bf7d3b1d8 rename Battle::Turn to Battle::Event 2025-03-24 21:08:42 -06:00
Bel LaPointe
dfc99e97e3 enqueue returns err on sanity checks, exec returns Turn for a series of turn results 2025-03-24 21:07:54 -06:00
Bel LaPointe
dcbdc29f78 drop unused battle::Idx 2025-03-24 20:20:54 -06:00
Bel LaPointe
fa3c80105f dmg 2025-03-20 21:38:08 -06:00
Bel LaPointe
f493a5e98c swap reveals i need a better targeting 2025-03-20 20:34:25 -06:00
Bel LaPointe
6007c2f2ed dont pass species but rather dex around 2025-03-20 20:12:32 -06:00
Bel LaPointe
50841b807a rename to mon 2025-03-20 20:06:09 -06:00
bel
9edb4a224c impl Battle::Engine::exec() 2025-03-14 00:11:11 -06:00
bel
262c962d5d hrrmmm 2025-03-13 21:11:52 -06:00
Bel LaPointe
5b60e34801 drop ref file 2025-03-13 21:03:49 -06:00
Bel LaPointe
e6fa3a9293 impl src::battler and src::mon which is Godot free 2025-03-13 21:03:20 -06:00
Bel LaPointe
f7f46ba96e can punch self 2025-03-02 18:07:14 -07:00
Bel LaPointe
ed5e6d4487 https://godot-rust.github.io/book/register/functions.html 2025-03-02 14:49:38 -07:00
Bel LaPointe
3893702330 wheeeee but also try signal 2025-03-02 14:39:37 -07:00
Bel LaPointe
a2f4fce705 finish hello world just to be sure physics process werks 2025-03-02 14:33:28 -07:00
Bel LaPointe
b04ab122eb https://godot-rust.github.io/book/intro/hello-world.html 2025-03-02 13:34:38 -07:00
Bel LaPointe
c2f05eaeb0 shuff 2025-03-02 12:49:09 -07:00
Bel LaPointe
4c2ab4a592 native db is just a key value store with more boilerplate but handy generics 2025-03-02 12:47:31 -07:00
22 changed files with 992 additions and 55 deletions

209
src/rust/Cargo.lock → src/gmon/Cargo.lock generated Normal file → Executable file
View File

@@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.96" version = "1.0.96"
@@ -186,6 +195,24 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "gdextension-api"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8707eca996b28193b772a4a9a28a97364bb93c97e3c313542e812f2963fb93"
[[package]]
name = "gensym"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82"
dependencies = [
"proc-macro2",
"quote",
"syn",
"uuid",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.1" version = "0.3.1"
@@ -198,12 +225,113 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "glam"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.2" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "gmon"
version = "0.1.0"
dependencies = [
"futures",
"godot",
"mon",
"native_db",
"native_model",
"serde",
]
[[package]]
name = "godot"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a29a3646d4b02bf4587fedba4ac7b44a47d45c933fd85ba7e61292408818eaa4"
dependencies = [
"godot-core",
"godot-macros",
]
[[package]]
name = "godot-bindings"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0014540bff93ba275031bc852f1cf9a3ff3216f02cdd51dc249745dccc8c578"
dependencies = [
"gdextension-api",
]
[[package]]
name = "godot-cell"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88184d525d763ebc88ff6806ffee35e19c7118d5c9e4eedbc74e70e069f8a671"
[[package]]
name = "godot-codegen"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa72d9b8be812fef2932f2a172b80c8b3feaee030571682f8f770c3d1c348d8"
dependencies = [
"godot-bindings",
"heck",
"nanoserde",
"proc-macro2",
"quote",
"regex",
]
[[package]]
name = "godot-core"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ff8345372e4c990ca592d7b61490ef88ff249fc77226f5b79beca173b4a0458"
dependencies = [
"glam",
"godot-bindings",
"godot-cell",
"godot-codegen",
"godot-ffi",
]
[[package]]
name = "godot-ffi"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "606d08085bd590d2f9c72633d4d218fee665ab3a0760b2d9daff2d964d628def"
dependencies = [
"gensym",
"godot-bindings",
"godot-codegen",
"libc",
"paste",
]
[[package]]
name = "godot-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c11b3188e54ebacf88feb4c968ed19048a71a329c4cfd4a06cf374f18357a36"
dependencies = [
"godot-bindings",
"proc-macro2",
"quote",
"venial",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.14" version = "1.0.14"
@@ -228,6 +356,25 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mon"
version = "0.1.0"
[[package]]
name = "nanoserde"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de9cf844ab1e25a0353525bd74cb889843a6215fa4a0d156fd446f4857a1b99"
dependencies = [
"nanoserde-derive",
]
[[package]]
name = "nanoserde-derive"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e943b2c21337b7e3ec6678500687cdc741b7639ad457f234693352075c082204"
[[package]] [[package]]
name = "native_db" name = "native_db"
version = "0.8.1" version = "0.8.1"
@@ -287,6 +434,12 @@ version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.16"
@@ -299,14 +452,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "price-is-wrong"
version = "0.1.0"
dependencies = [
"futures",
"native_db",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.93" version = "1.0.93"
@@ -354,6 +499,35 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.44" version = "0.38.44"
@@ -504,6 +678,25 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
[[package]]
name = "uuid"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
dependencies = [
"getrandom",
]
[[package]]
name = "venial"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a42528baceab6c7784446df2a10f4185078c39bf73dc614f154353f1a6b1229"
dependencies = [
"proc-macro2",
"quote",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"

15
src/gmon/Cargo.toml Executable file
View File

@@ -0,0 +1,15 @@
[package]
name = "gmon"
version = "0.1.0"
edition = "2024"
[dependencies]
futures = "0.3.31"
godot = "0.2.4"
native_db = "0.8.1"
native_model = "0.4.20"
serde = "1.0.218"
mon = { path = "../mon" }
[lib]
crate-type = ["cdylib"]

26
src/gmon/src/battle.rs Normal file
View File

@@ -0,0 +1,26 @@
use ::mon::*;
use godot::classes::{ISprite2D, Sprite2D};
use godot::prelude::*;
pub type Instance = mon::battle::Instance;
pub type Team = mon::battle::Team;
pub type Event = mon::battle::Event;
pub type Move = mon::battle::Move;
#[derive(GodotClass)]
#[class(base=Sprite2D)]
struct Engine {
instance: mon::battle::Engine,
base: Base<Sprite2D>,
}
#[godot_api]
impl ISprite2D for Engine {
fn init(base: Base<Sprite2D>) -> Self {
Self {
instance: mon::battle::Engine::new(),
base: base,
}
}
}

9
src/gmon/src/lib.rs Executable file
View File

@@ -0,0 +1,9 @@
use godot::prelude::*;
struct MyExtension;
#[gdextension]
unsafe impl ExtensionLibrary for MyExtension {}
pub mod battle;
pub mod mon;

5
src/gmon/src/mon.rs Normal file
View File

@@ -0,0 +1,5 @@
use ::mon::*;
pub type Instance = mon::mon::Instance;
pub type Species = mon::mon::Species;
pub type Dex = mon::mon::Dex;

View File

@@ -1,4 +1,4 @@
# Price is Wrong # Mon
Like https://gitea.inhome.blapointe.com/bel/price-is-wrong but Like https://gitea.inhome.blapointe.com/bel/price-is-wrong but

View File

@@ -0,0 +1,14 @@
[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.1
reloadable = true
[libraries]
linux.debug.x86_64 = "res://../gmon/target/debug/libgmon.so"
linux.release.x86_64 = "res://../gmon/target/release/libgmon.so"
windows.debug.x86_64 = "res://../gmon/target/debug/gmon.dll"
windows.release.x86_64 = "res://../gmon/target/release/gmon.dll"
macos.debug = "res://../gmon/target/debug/libgmon.dylib"
macos.release = "res://../gmon/target/release/libgmon.dylib"
macos.debug.arm64 = "res://../gmon/target/debug/libgmon.dylib"
macos.release.arm64 = "res://../gmon/target/release/libgmon.dylib"

View File

@@ -0,0 +1,16 @@
extends BreeLPlayer
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_speed_increased() -> void:
print("SPEED INCREASED")
pass # Replace with function body.

16
src/godot/gd/button.gd Normal file
View File

@@ -0,0 +1,16 @@
extends Button
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_button_up() -> void:
print("button up")
get_parent().get_parent().tackled()

10
src/godot/gd/name.gd Normal file
View File

@@ -0,0 +1,10 @@
extends RichTextLabel
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
self.text = get_parent().mon_name()
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

58
src/godot/gd/node_2d.tscn Normal file
View File

@@ -0,0 +1,58 @@
[gd_scene load_steps=5 format=3 uid="uid://dg85xsdqg6wj0"]
[ext_resource type="Texture2D" uid="uid://cneqjngiworik" path="res://icon.svg" id="1_swwii"]
[ext_resource type="Script" path="res://gd/texture_progress_bar.gd" id="2_2jm7r"]
[ext_resource type="Script" path="res://gd/name.gd" id="3_u8x5l"]
[ext_resource type="Script" path="res://gd/button.gd" id="4_iyywh"]
[node name="Node2D" type="Node2D"]
[node name="Camera2D" type="Camera2D" parent="."]
position = Vector2(2, 0)
[node name="Sprite2D" type="Sprite2D" parent="."]
z_index = -10
position = Vector2(-9.53674e-06, 4.29153e-06)
scale = Vector2(9, 5.0625)
texture = ExtResource("1_swwii")
[node name="attacker" type="Mon" parent="."]
position = Vector2(-408.5, 157.5)
scale = Vector2(2.41406, 2.41406)
[node name="TextureProgressBar" type="TextureProgressBar" parent="attacker"]
z_index = 10
offset_right = 40.0
offset_bottom = 40.0
texture_progress = ExtResource("1_swwii")
script = ExtResource("2_2jm7r")
[node name="name" type="RichTextLabel" parent="attacker"]
z_index = 10
offset_right = 348.0
offset_bottom = 40.0
text = "asdf"
script = ExtResource("3_u8x5l")
[node name="sprite" type="Sprite2D" parent="attacker"]
texture = ExtResource("1_swwii")
[node name="actions" type="Node2D" parent="attacker"]
position = Vector2(169.217, -65.2427)
scale = Vector2(0.414239, 0.414239)
[node name="button" type="Button" parent="attacker/actions"]
offset_left = 435.0
offset_top = 180.0
offset_right = 435.0
offset_bottom = 180.0
icon = ExtResource("1_swwii")
script = ExtResource("4_iyywh")
[node name="opponent" type="Sprite2D" parent="."]
position = Vector2(420, -166)
scale = Vector2(2.41406, 2.41406)
texture = ExtResource("1_swwii")
[connection signal="hp_changed" from="attacker" to="attacker/TextureProgressBar" method="_on_mon_hp_changed"]
[connection signal="button_up" from="attacker/actions/button" to="attacker/actions/button" method="_on_button_up"]

View File

@@ -0,0 +1,15 @@
extends TextureProgressBar
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_mon_hp_changed() -> void:
self.value = 100 * get_parent().hp()

View File

@@ -0,0 +1,14 @@
extends TouchScreenButton
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_pressed() -> void:
print("pressed")

View File

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 994 B

View File

@@ -10,7 +10,8 @@ config_version=5
[application] [application]
config/name="price-is-wrong" config/name="mon"
run/main_scene="res://gd/node_2d.tscn"
config/features=PackedStringArray("4.3", "GL Compatibility") config/features=PackedStringArray("4.3", "GL Compatibility")
config/icon="res://icon.svg" config/icon="res://icon.svg"

7
src/mon/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "mon"
version = "0.1.0"

8
src/mon/Cargo.toml Executable file
View File

@@ -0,0 +1,8 @@
[package]
name = "mon"
version = "0.1.0"
edition = "2024"
[dependencies]
[lib]

1
src/mon/src/lib.rs Executable file
View File

@@ -0,0 +1 @@
pub mod mon;

574
src/mon/src/mon.rs Executable file
View File

@@ -0,0 +1,574 @@
pub mod mon {
#[derive(Clone, Debug, PartialEq)]
pub struct Species {
pub name: String,
pub hp: i32,
pub atk: i32,
pub def: i32,
pub spd: i32,
}
#[cfg(test)]
mod species_tests {
use super::*;
#[test]
fn test_literal() {
let _: Species = Species {
name: "".to_string(),
hp: 1,
atk: 2,
def: 3,
spd: 4,
};
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Dex {
Pika,
Mari,
}
impl Dex {
pub fn new(&self) -> Species {
self.clone().into()
}
pub fn name(&self) -> String {
self.new().name
}
pub fn hp(&self) -> i32 {
self.new().hp
}
pub fn atk(&self) -> i32 {
self.new().atk
}
pub fn def(&self) -> i32 {
self.new().def
}
pub fn spd(&self) -> i32 {
self.new().spd
}
}
impl Into<Species> for Dex {
fn into(self) -> Species {
match self {
Dex::Pika => Species {
name: "Pika".to_string(),
hp: 10,
atk: 11,
def: 9,
spd: 11,
},
Dex::Mari => Species {
name: "Mari".to_string(),
hp: 10,
atk: 8,
def: 12,
spd: 9,
},
}
}
}
#[cfg(test)]
mod dex_tests {
use super::*;
#[test]
fn test_dex_to_species() {
let dex = Dex::Pika;
let _: Species = dex.new();
let _: Species = dex.into();
let _: Species = Dex::Pika.new();
let _: Species = Dex::Pika.into();
}
}
#[derive(Clone, Debug)]
pub struct Instance {
pub dex: Dex,
pub damage: i32,
}
impl Instance {
pub fn roll(dex: Dex) -> Instance {
Instance {
dex: dex,
damage: 0,
}
}
pub fn hit(&mut self, damage: i32) {
let cap = self.dex.new().hp;
self.damage = match self.damage + damage {
v if v < cap => v,
_ => cap,
};
}
}
#[cfg(test)]
mod instance_tests {
use super::*;
#[test]
fn test_instance_roll() {
let i: Instance = Instance::roll(Dex::Pika);
assert_eq!(0, i.damage);
assert_eq!(Dex::Pika, i.dex);
}
}
}
pub mod battle {
use super::*;
pub struct Engine {
teams: Vec<Team>,
q: Vec<Move>,
}
impl Engine {
pub fn new(first: Vec<mon::Instance>, second: Vec<mon::Instance>) -> Engine {
let mut result = Self {
teams: vec![],
q: vec![],
};
result.join(first);
result.join(second);
result
}
pub fn join(&mut self, third: Vec<mon::Instance>) {
self.teams.push(Team::new(third));
}
pub fn enqueue(&mut self, m: Move) -> Result<(), String> {
match m {
Move::Attack(w_idx, r_idx) => {
if self.teams()[w_idx].lost {
return Err(format!("team[{w_idx}] lost"));
}
if !self.teams()[w_idx].any_can_attack() {
return Err(format!("team[{w_idx}] cannot attack"));
}
if !self.teams()[r_idx].any_can_be_attacked() {
return Err(format!("team[{r_idx}] cannot be attacked"));
}
}
Move::Pass(team_idx) => {
if self.teams()[team_idx].lost {
return Err(format!("team[{team_idx}] lost"));
}
if !self.teams()[team_idx].any_can_attack() {
return Err(format!("team[{team_idx}] cannot move"));
}
}
Move::Swap(team_idx, mon_idx) => {
if self.teams()[team_idx].lost {
return Err(format!("team[{team_idx}] lost"));
}
if !self.teams()[team_idx].mons()[mon_idx].alive() {
return Err(format!("team[{team_idx}].mons[{mon_idx}] cannot swap in"));
}
if self.teams()[team_idx].mons()[mon_idx].out {
return Err(format!("team[{team_idx}].mons[{mon_idx}] already in"));
}
}
};
self.q.push(m);
Ok(())
}
pub fn exec(&mut self) -> Vec<Event> {
self.turn_order().iter().for_each(|idx| {
match self.q[*idx].clone() {
Move::Pass(_) => {}
Move::Attack(r_team_idx, w_team_idx) => {
let r_team = self.teams[r_team_idx].clone();
let w_team = &mut self.teams[w_team_idx];
for r_mon_idx in 0..r_team.mons().len() {
let r_mon = &r_team.mons()[r_mon_idx];
if !r_mon.can_attack() {
continue;
}
for w_mon_idx in 0..w_team.mons().len() {
let w_mon = &w_team.mons()[w_mon_idx];
if !w_mon.can_be_attacked() {
continue;
}
let r_mon = &r_mon.mon;
let w_mon = &w_mon.mon;
w_team.mons[w_mon_idx].mon = Engine::attack(w_mon, r_mon);
w_team.mons[w_mon_idx].out =
w_team.mons[w_mon_idx].out && w_team.mons[w_mon_idx].alive()
}
}
self.teams[w_team_idx] = w_team.to_owned();
}
Move::Swap(team_idx, mon_idx) => {
let mut team = self.teams[team_idx].clone();
team.mons = team
.mons()
.iter()
.map(|m| {
let mut m = m.clone();
m.out = false;
m
})
.collect();
team.mons[mon_idx].out = true;
self.teams[team_idx] = team;
}
};
});
self.new_turn()
}
pub fn teams(&self) -> Vec<Team> {
self.teams.iter().map(|t| t.clone()).collect()
}
fn new_turn(&mut self) -> Vec<Event> {
self.q = vec![];
let mut result = vec![];
let teams = self.teams();
let mut teams_not_lost = vec![];
for i in 0..teams.len() {
let has_mon_out = teams[i].mons().iter().filter(|m| m.out).count() > 0;
let has_mon_alive = teams[i].mons().iter().filter(|m| m.alive()).count() > 0;
let lost_before = teams[i].lost;
if !has_mon_out && has_mon_alive {
result.push(Event::MustSwap(i));
} else if !has_mon_alive && !lost_before {
self.teams[i].lost = true;
result.push(Event::Lose(i));
}
if !self.teams()[i].lost {
teams_not_lost.push(i);
}
}
if teams_not_lost.len() == 1 {
result.push(Event::Win(teams_not_lost[0]));
}
result
}
fn turn_order(&self) -> Vec<usize> {
let mut order = vec![];
for i in 0..self.q.len() {
order.push((
i,
match &self.q[i] {
Move::Pass(_) => 0,
Move::Swap(_, _) => -10,
Move::Attack(r_team, _) => self.teams[r_team.to_owned()]
.mons
.iter()
.filter(|m| m.can_attack())
.map(|m| m.mon.dex.new().spd)
.collect::<Vec<_>>()
.first()
.expect("team had no pokemon to attack")
.to_owned(),
},
));
}
order.sort_unstable_by(|a, b| a.1.cmp(&b.1));
order.iter().map(|x| x.0).collect()
}
fn attack(w: &mon::Instance, r: &mon::Instance) -> mon::Instance {
let mut w = w.clone();
let atk_delta = r.dex.new().atk - 10;
let def_delta = w.dex.new().def - 10;
let power = match atk_delta - def_delta {
v if v < 1 => 1,
v => v,
};
w.hit(power);
w
}
}
#[cfg(test)]
mod engine_tests {
use super::*;
#[test]
fn test_new() {
let mut engine = Engine::new(team_a(), team_b());
engine.join(team_b());
}
#[test]
fn test_attack() {
let mut pika = mon::Instance::roll(mon::Dex::Pika);
let mut mari = mon::Instance::roll(mon::Dex::Mari);
pika = Engine::attack(&mut pika, &mut mari);
assert_eq!(1, pika.damage);
mari = Engine::attack(&mut mari, &mut pika);
assert_eq!(1, mari.damage);
for _ in 0..pika.dex.new().hp + 5 {
pika = Engine::attack(&mut pika, &mut mari);
}
assert_eq!(pika.dex.new().hp, pika.damage);
assert_eq!(1, mari.damage);
}
#[test]
fn test_turn() {
let mut engine = Engine::new(team_a(), team_b());
engine.join(team_b());
// player 0 mon 0 attacks team 1 mon 0
engine
.enqueue(Move::Attack(0, 1))
.expect("failed to attack");
// player 1 mon 0 passes
engine.enqueue(Move::Pass(1)).expect("failed to pass");
// player 1 mon 1 not out
assert_eq!(0, engine.exec().len());
assert_eq!(0, engine.teams()[0].mons()[0].mon.damage);
assert_eq!(1, engine.teams()[1].mons()[0].mon.damage);
assert_eq!(0, engine.teams()[1].mons()[1].mon.damage);
}
fn team_a() -> Vec<mon::Instance> {
vec![mon::Instance::roll(mon::Dex::Pika)]
}
fn team_b() -> Vec<mon::Instance> {
vec![
mon::Instance::roll(mon::Dex::Mari),
mon::Instance::roll(mon::Dex::Pika),
]
}
}
#[derive(Clone, Debug)]
pub struct Team {
mons: Vec<Instance>,
lost: bool,
}
impl Team {
fn new(mons: Vec<mon::Instance>) -> Self {
assert!(mons.len() > 0);
let mut mons: Vec<_> = mons
.iter()
.map(|m| Instance {
mon: m.clone(),
out: false,
})
.collect();
mons[0].out = true;
Self {
mons: mons.clone(),
lost: mons.len() == 0,
}
}
pub fn mons(&self) -> Vec<Instance> {
self.mons.iter().map(|i| i.clone()).collect()
}
fn any_can_attack(&self) -> bool {
self.mons().iter().filter(|m| m.can_attack()).count() > 0
}
fn any_can_be_attacked(&self) -> bool {
self.mons().iter().filter(|m| m.can_be_attacked()).count() > 0
}
}
#[derive(Clone, Debug)]
pub struct Instance {
pub mon: mon::Instance,
pub out: bool,
}
impl Instance {
fn alive(&self) -> bool {
self.damage() < self.mon.dex.new().hp
}
fn can_attack(&self) -> bool {
self.out && self.alive()
}
fn can_be_attacked(&self) -> bool {
self.out && self.alive()
}
pub fn damage(&self) -> i32 {
self.mon.damage
}
}
#[cfg(test)]
mod instance_tests {
use super::*;
#[test]
fn test_checks() {
let mut i = Instance {
mon: mon::Instance::roll(mon::Dex::Pika),
out: true,
};
assert_eq!(true, i.alive());
assert_eq!(true, i.can_attack());
assert_eq!(true, i.can_be_attacked());
i.mon.damage = i.mon.dex.new().hp;
assert_eq!(false, i.alive());
assert_eq!(false, i.can_attack());
assert_eq!(false, i.can_be_attacked());
}
}
#[derive(Clone, Debug)]
pub enum Move {
Pass(usize),
Attack(usize, usize),
Swap(usize, usize),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Event {
MustSwap(usize),
Lose(usize),
Win(usize),
}
}
#[cfg(test)]
mod mon_tests {
use super::*;
#[test]
fn test() {
let mut my_mons = vec![
mon::Instance::roll(mon::Dex::Pika),
mon::Instance::roll(mon::Dex::Mari),
];
my_mons[0].hit(5);
let they_mons = vec![
mon::Instance::roll(mon::Dex::Mari),
mon::Instance::roll(mon::Dex::Mari),
mon::Instance::roll(mon::Dex::Mari),
];
let mut engine = battle::Engine::new(my_mons, they_mons);
// team0mon0 trades attacks with team1mon0
engine
.enqueue(battle::Move::Attack(1, 0))
.expect("failed to attack");
engine
.enqueue(battle::Move::Attack(0, 1))
.expect("failed to attack");
assert_eq!(0, engine.exec().len());
assert_eq!(6, engine.teams()[0].mons()[0].damage());
assert_eq!(0, engine.teams()[0].mons()[1].damage());
assert_eq!(1, engine.teams()[1].mons()[0].damage());
assert_eq!(0, engine.teams()[1].mons()[1].damage());
assert_eq!(0, engine.teams()[1].mons()[2].damage());
// team0mon0 swaps with team0mon0
// team1mon0 attacks team0
engine
.enqueue(battle::Move::Attack(1, 0))
.expect("failed to attack");
engine
.enqueue(battle::Move::Swap(0, 1))
.expect("failed to swap");
assert_eq!(0, engine.exec().len());
assert_eq!(6, engine.teams()[0].mons()[0].damage());
assert_eq!(1, engine.teams()[0].mons()[1].damage());
assert_eq!(1, engine.teams()[1].mons()[0].damage());
assert_eq!(0, engine.teams()[1].mons()[1].damage());
assert_eq!(0, engine.teams()[1].mons()[2].damage());
// team1mon0 wears down team0mon1
for i in 2..engine.teams()[0].mons()[1].mon.dex.hp() {
engine
.enqueue(battle::Move::Attack(1, 0))
.expect("failed to attack");
assert_eq!(0, engine.exec().len());
assert_eq!(i, engine.teams()[0].mons()[1].damage());
}
// team1mon0 downs team0mon1
engine
.enqueue(battle::Move::Attack(1, 0))
.expect("failed to kill");
let turn_results = engine.exec();
assert_eq!(1, turn_results.len());
assert_eq!(battle::Event::MustSwap(0), turn_results[0]);
assert_eq!(
engine.teams()[0].mons()[1].mon.dex.hp(),
engine.teams()[0].mons()[1].damage()
);
assert!(!engine.teams()[0].mons()[1].out);
// team0 must swap
assert!(engine.enqueue(battle::Move::Attack(0, 1)).is_err());
assert!(engine.enqueue(battle::Move::Pass(0)).is_err());
engine
.enqueue(battle::Move::Swap(0, 0))
.expect("failed to swap on MustSwap");
assert!(engine.enqueue(battle::Move::Attack(1, 0)).is_err());
assert_eq!(0, engine.exec().len());
// team1mon0 wears down team0mon0
for i in 7..engine.teams()[0].mons()[0].mon.dex.hp() {
engine
.enqueue(battle::Move::Attack(1, 0))
.expect("failed to attack");
assert_eq!(0, engine.exec().len());
assert_eq!(i, engine.teams()[0].mons()[0].damage());
}
// team1mon0 downs team0mon0
// team0 loses
// team1 wins
engine
.enqueue(battle::Move::Attack(1, 0))
.expect("failed to kill");
let turn_results = engine.exec();
assert_eq!(2, turn_results.len(), "{:?}", turn_results);
assert_eq!(
battle::Event::Lose(0),
turn_results[0],
"{:?}",
turn_results
);
assert_eq!(battle::Event::Win(1), turn_results[1], "{:?}", turn_results);
assert_eq!(
engine.teams()[0].mons()[0].mon.dex.hp(),
engine.teams()[0].mons()[0].damage()
);
}
}

View File

@@ -1,8 +0,0 @@
[package]
name = "price-is-wrong"
version = "0.1.0"
edition = "2024"
[dependencies]
futures = "0.3.31"
native_db = "0.8.1"

View File

@@ -1,37 +0,0 @@
mod main {
use {
};
pub fn run() {
let mut db = DB::new(false);
db.init().expect("db init failed");
}
struct DB {
db: bool,
}
impl DB {
fn new(db: bool) -> DB {
DB{db: db}
}
fn init(self: &mut DB) -> Result<(), String> {
//self.exec("DROP TABLE IF EXISTS t")?;
//self.exec("DROP TABLE IF EXISTS t2")?;
//self.exec("CREATE TABLE t (id INTEGER PRIMARY KEY, k TEXT, v TEXT)")?;
//self.exec("CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t (id))")?;
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mvp() {
main::run();
}
}