diff --git a/tetris/Cargo.lock b/tetris/Cargo.lock index 98af9a7..3c21855 100644 --- a/tetris/Cargo.lock +++ b/tetris/Cargo.lock @@ -20,6 +20,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.3" @@ -41,12 +47,29 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "colored" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +dependencies = [ + "is-terminal", + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -82,6 +105,27 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fnv" version = "1.0.7" @@ -171,6 +215,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + [[package]] name = "http" version = "0.2.8" @@ -198,11 +248,22 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] @@ -213,11 +274,23 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" -version = "0.2.132" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" @@ -253,7 +326,74 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.36.1", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", ] [[package]] @@ -271,7 +411,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -301,7 +441,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -391,7 +531,20 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", ] [[package]] @@ -497,9 +650,12 @@ dependencies = [ name = "tetris" version = "0.1.0" dependencies = [ + "colored", "futures-util", "itertools", "log", + "num", + "num_cpus", "polynomial", "rand", "serde", @@ -692,39 +848,105 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", ] +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/tetris/Cargo.toml b/tetris/Cargo.toml index 7ebe165..cb8197c 100644 --- a/tetris/Cargo.toml +++ b/tetris/Cargo.toml @@ -7,7 +7,10 @@ edition = "2021" [dependencies] rand = "0.8.5" polynomial = "0.2.4" -itertools = "0.10.5" +itertools = "0.11.0" +colored = "2.0.4" +num = "0.4.1" +num_cpus = "1.0" tungstenite = "*" serde = { version = "1.0.78", features = ["derive"] } diff --git a/tetris/src/board.rs b/tetris/src/board.rs index 44a4cdd..dcf2788 100644 --- a/tetris/src/board.rs +++ b/tetris/src/board.rs @@ -2,9 +2,13 @@ use crate::constants::board_constants::*; use crate::constants::types::*; -use crate::piece::Piece; +use crate::piece::*; +use crate::queue::{piece_type_to_block_colored}; use crate::point_vector::{Point, PointVector}; use std::fmt::{Display, Formatter}; +use colored::*; +use num::clamp; +use crate::weight::Weights; #[derive(Debug, Clone)] pub struct Board { @@ -14,7 +18,7 @@ pub struct Board { impl Default for Board { fn default() -> Self { Self { - arr: [0; BOARD_WIDTH] + arr: [0; BOARD_WIDTH], } } } @@ -101,6 +105,24 @@ impl Board { } } + pub fn add_garbage(&mut self, col: usize, amount: usize) { + let highest = BOARD_HEIGHT; + + for r in 0..highest-amount+1 { + self._set_row(highest-r, self.get_row(highest-r-amount)); + } + + for n in 0..amount { + for w in 0..BOARD_WIDTH { + if w == col { + self.arr[w] &= !(1 << n); + } else { + self.arr[w] |= 1 << n; + } + } + } + } + pub fn remove(&mut self, row: usize, col: usize) { self.arr[col] &= !(1 << row); } @@ -276,6 +298,67 @@ impl Board { (holes_count_total, holes_count_weighted, cell_covered_count) } + pub fn horizontal_holes_weighted(&self, weight: &Weights) -> f32 { + let mut holes_count_total = 0; + let mut holes_count_weighted = 0; + let mut finalweight = 0.0; + + for row in 0..self.get_max_height() { + // only counting when you find a filled block + let mut counting = true; + + for col in 0..BOARD_WIDTH { + let spot = self.get(row, col); + // hole + if !spot { + holes_count_total += 1; + if counting { + holes_count_weighted += 1; + counting = false; + } + } else { + counting = true; + } + } + finalweight += (holes_count_total as f32)*0.0 + weight.holes_per_row_weighted_weight.eval(holes_count_weighted as f32); + holes_count_total = 0; + holes_count_weighted = 0; + } + + finalweight + } + + pub fn count_horizontal_holes(&self) -> f32 { + let mut holes_count_total = 0; + let mut holes_count_weighted = 0; + let mut finalcount = 0.0; + + for row in 0..self.get_max_height() { + // only counting when you find a filled block + let mut counting = true; + + for col in 0..BOARD_WIDTH { + let spot = self.get(row, col); + // hole + if !spot { + holes_count_total += 1; + if counting { + holes_count_weighted += 1; + counting = false; + } + } else { + counting = true; + } + } + finalcount += holes_count_weighted as f32; + println!("row {} has {} weighted holes, {} holes", row, holes_count_weighted, holes_count_total); + holes_count_total = 0; + holes_count_weighted = 0; + } + + finalcount + } + fn check_hor_t(arr: Vec) -> bool { // only for horizontal t slots rn @@ -287,9 +370,55 @@ impl Board { // 0 0 0 // 1 0 1 - arr == [0b101, 0b000, 0b001] || arr == [0b001, 0b000, 0b101] - // arr == vec![5, 0, 1] || arr == vec![1, 0, 5] + // 1 0 1 + // 0 0 0 + // 1 0 1 + + // 1 0 1 + // 1 0 0 + // 1 0 1 + + // 1 0 1 + // 0 0 1 + // 1 0 1 + + arr == [0b101, 0b000, 0b001] || arr == [0b001, 0b000, 0b101] || arr == [0b101, 0b000, 0b101] || + arr == [0b111, 0b000, 0b101] || arr == [0b101, 0b000, 0b111] + } + fn check_special_t(arr: Vec) -> bool { + // special tspins + + // 1 1 0 0 1 + // 1 0 0 0 1 + // 1 0 1 1 1 + // 1 0 0 1 1 + // 1 0 1 1 1 + + arr == [0b11111, 0b10000, 0b00101, 0b00111, 0b11111] || arr == [0b11111, 0b00111, 0b00101, 0b10000, 0b11111] || + + // 1 1 0 0 0 + // 1 0 0 0 0 + // 1 0 1 1 1 + // 1 0 0 1 1 + // 1 0 1 1 1 + + arr == [0b11111, 0b10000, 0b00101, 0b00111, 0b00111] || arr == [0b00111, 0b00111, 0b00101, 0b10000, 0b11111] || + // 1 1 0 0 1 + // 1 0 0 0 1 + // 1 0 1 1 1 + // 1 0 0 1 1 + // 1 0 0 1 1 + + arr == [0b11111, 0b10000, 0b00100, 0b00111, 0b11111] || arr == [0b11111, 0b00111, 0b00100, 0b10000, 0b11111] || + + // 1 1 0 0 0 + // 1 0 0 0 0 + // 1 0 1 1 1 + // 1 0 0 1 1 + // 1 0 0 1 1 + + arr == [0b11111, 0b10000, 0b00100, 0b00111, 0b00111] || arr == [0b00111, 0b00111, 0b00100, 0b10000, 0b11111] } pub fn t_slot(&self) -> usize { let h = self.get_max_height(); @@ -300,18 +429,55 @@ impl Board { } let mut out = 0; - for row in l..(h-3) { - let mask = 0b111 << row; + for row in l..=(h-3) { + let mask = 0b111; for columns in self.arr.windows(3) { // create a 3x3 grid - let columns: Vec = columns.iter().map(|x| x & mask).collect(); + let columns: Vec = columns.iter().map(|x| x >> row & mask).collect(); // checks if it is a t slot out += Board::check_hor_t(columns) as usize; } } + + if h - l < 5 { + return out + } + + for row in l..=(h-5) { + let mask = 0b11111; + for columns in self.arr.windows(5) { + // create a 5x5 grid + let columns: Vec = columns.iter().map(|x| x >> row & mask).collect(); + + // checks if it is a t slot + out += Board::check_special_t(columns) as usize; + } + } + out + } + pub fn special_t_slot(&self) -> usize { + let h = self.get_max_height(); + let l = self.get_min_height(); + + if h - l < 5 { + return 0 + } + + let mut out = 0; + for row in l..=(h-5) { + let mask = 0b11111; + for columns in self.arr.windows(5) { + // create a 5x5 grid + let columns: Vec = columns.iter().map(|x| x >> row & mask).collect(); + + // checks if it is a t slot + out += Board::check_special_t(columns) as usize; + } + } + out } pub fn get_max_height_difference(&self) -> usize { @@ -357,9 +523,9 @@ impl Board { if self.get(row, col) { out.push_str("■ "); } else if locations.contains(&Point(row as i8, col as i8)) { - out.push_str("⬚ "); + out.push_str(&format!("{} ", piece_type_to_block_colored(active_piece.get_type()))); } else { - out.push_str("□ "); + out.push_str(&format!("{} ", ("▫".truecolor(clamp(8*row,0,255).try_into().unwrap(),clamp(100-4*row,0,255).try_into().unwrap(),clamp(40,0,255).try_into().unwrap())))); } } out.push_str("\n"); diff --git a/tetris/src/bot.rs b/tetris/src/bot.rs index 100b6be..bc707ef 100644 --- a/tetris/src/bot.rs +++ b/tetris/src/bot.rs @@ -17,14 +17,18 @@ use itertools::{izip, Itertools}; use crate::communications::Suggestion; use crate::{Dependency, Opener, OpenerStatus, Point}; use crate::book::openers; -use crate::constants::board_constants::BOARD_WIDTH; +use crate::constants::board_constants::{BOARD_WIDTH, MAX_PLACE_HEIGHT}; +use crate::constants::queue_constants::MIN_QUEUE_LENGTH; use crate::point_vector::PointVector; +use num::clamp; +use num::traits::Pow; pub struct Bot { game: Game, weight: Weights, - opener: Opener + opener: Opener, + next_placements: PlacementList, } impl Display for Bot { @@ -40,6 +44,7 @@ impl Default for Bot { game: Game::new(None), weight: Weights::default(), opener: Opener::default(), + next_placements: PlacementList::new(), } } } @@ -57,6 +62,23 @@ impl Player for Bot { // R, C let mut action = vec![]; + if self.next_placements.len() > 0 { + println!("{:?}", self.next_placements); + match Bot::moves_to_placement(&mut self.get_game().clone(), &self.next_placements[0]) { + Ok(m) => { + action = m; + action.push(Command::HardDrop); + self.next_placements.remove(0); + + return action; + }, + Err(_) => { + eprintln!("error"); + self.next_placements = PlacementList::new(); + } + } + } + if self.opener.status == OpenerStatus::New { let mut sequence = vec![self.get_game().active_piece.piece_type]; sequence.append(&mut self.get_game().piece_queue.get_vec()); @@ -78,7 +100,8 @@ impl Player for Bot { // thread::sleep(time::Duration::from_millis(250)); - let (deep_moves, places, deep_scores) = self.move_placement_score(11, &self.weight.clone()); + // println!("{}", self.get_game().board.get_max_height()); + let (deep_moves, places, deep_scores) = self.move_placement_score(MOVEPLACEMENTSCORE - self.get_game().board.get_max_height()*PANICBURST, &self.weight.clone()); let deep_scores: Vec = deep_scores .iter() .map(|(board, versus)| board + versus) @@ -96,11 +119,14 @@ impl Player for Bot { } - println!("{:?}", action); - println!("{}", min_score); - println!("{}", p[0]); - if min_score < -10000.0{ - println!("{:?}", p); + // println!("{:?}", action); + // println!("{}", min_score); + // println!("{}", p[0]); + // println!("{:?}", p); + if min_score < -100000.0{ + self.next_placements = p; + self.next_placements.remove(0); + println!("{:?}", self.next_placements); } action.push(Command::HardDrop); @@ -164,7 +190,7 @@ impl Bot { pub fn move_placement_score( &mut self, - depth: usize, + mut depth: usize, weights: &Weights, ) -> (MoveList, Vec, ScoreList) { let mut dummy = self.game.clone(); @@ -182,7 +208,8 @@ impl Bot { let mut next_scores = ScoreList::new(); //pruning parameters - let n = 50; + let mut n = 600 - clamp(self.game.get_paranoia(), 0.0, 4.0) as usize * 100; + if self.game.should_panic() { n = 80; } let prune_depth = 1; for curr_depth in 1..depth { @@ -200,7 +227,7 @@ impl Bot { .unzip(); curr_scores.sort_by(|(v1, b1), (v2, b2)| (v1 + b1).partial_cmp(&(v2 + b2)).unwrap()); - curr_scores.truncate(n); + curr_scores.truncate(1 + n - n * (4*curr_depth) / (5*depth)); } //generating next_mps @@ -231,7 +258,7 @@ impl Bot { next_moves.push(one_move.clone()); next_placements.push(placements.clone()); - next_scores.push((board, (versus + add_versus) * (1.0-(0.5*curr_depth as f32/depth as f32)))); + next_scores.push((board, (versus + add_versus) * (1.0-(0.25*curr_depth as f32/depth as f32)))); } } curr_moves = mem::take(&mut next_moves); @@ -429,9 +456,9 @@ impl Bot { //TODO: put all the logic in nice places (scorer class?) // PC (pseudo) PRUNING - if false && (game.board.get_mino_count() % 2 == 0) { + /*if false && (game.board.get_mino_count() % 2 == 0) { // pseudo pruning, gives a large penalty for unsolvable/hard to solve boards - let penalty = (100000.0, Bot::score_board(&game.board, weights)); + let penalty = (100.0, Bot::score_board(&game, &game.board, weights)); let target_height = 4 + (game.board.get_mino_count() % 4)/2; @@ -466,29 +493,36 @@ impl Bot { if parity.1 == false && !(usable_queue.contains(&1) || usable_queue.contains(&5) || usable_queue.contains(&6) || !game.board.get_min_height() >= 2){ return penalty } - } + }*/ return ( - Bot::score_board(&game.board, weights), - Bot::score_versus(&game.game_data, weights), + Bot::score_board(&game, &game.board, weights), + Bot::score_versus(&game, &game.game_data, weights), ) } - fn score_board(board: &Board, weights: &Weights) -> Score { - Bot::get_holes_and_cell_covered_score(board, weights) - + Bot::get_height_score(board, weights) + fn score_board(game: &Game, board: &Board, weights: &Weights) -> Score { + Bot::get_holes_and_cell_covered_score(board, weights, game.should_panic()) + + Bot::get_height_score(board, weights, game.should_panic()) + Bot::get_height_differences_score(board, weights) - + Bot::get_t_slot_score(board, weights) + + Bot::get_t_slot_score(game, board, weights, game.should_panic()) + + Bot::top_of_board_score(game, board) } - fn score_versus(game_data: &GameData, weight: &Weights) -> Score { + fn score_versus(game: &Game, game_data: &GameData, weight: &Weights) -> Score { // let spin = Game::get_t_spin_type(piece, board); - let combo_score = weight.combo_weight.eval(game_data.combo as f32); - let b2b = weight.b2b_weight.eval(game_data.b2b as f32); - let attack = weight.damage_weight.eval(game_data.last_sent as f32); + let mut combo_score = weight.combo_weight.eval(game_data.combo as f32); + let mut b2b = weight.b2b_weight.eval(game_data.b2b as f32); + let mut attack = weight.damage_weight.eval(game_data.last_sent as f32); + if game.should_panic() { + combo_score = weight.panic_combo_weight.eval(game_data.combo as f32); + b2b = weight.panic_b2b_weight.eval(game_data.b2b as f32); + attack = weight.panic_damage_weight.eval(game_data.last_sent as f32); + } let clear = weight.clear_weight.eval(game_data.last_cleared as f32); let pc = game_data.all_clear; let t_spin = game_data.t_spin; + let paranoia = game.get_paranoia() * weight.paranoia_weight; let mut extra = 0.0; @@ -496,10 +530,23 @@ impl Bot { extra -= 1000000.0; } if t_spin { - extra -= 100.0 + extra -= weight.tspin_reward*weight.tspin_reward_expo.pow(game_data.last_cleared as f32); + if game.should_panic() { + extra -= weight.panic_tspin_reward*weight.panic_tspin_reward_expo.pow(game_data.last_cleared as f32); + } } - - combo_score + b2b + attack + clear + extra + if game_data.last_placed_piece.get_type() == 6 { + let mut wtw = weight.waste_t_weight; + if game.should_panic() { wtw = weight.panic_waste_t_weight; } + match game_data.t_spin_type { + 0 => extra += wtw, + 1 => extra += wtw * clamp(2 - game_data.last_cleared, 0, 1) as f32, // doubles and triples should not be punished + 2 => extra += wtw * 0.9 * clamp(2 - game_data.last_cleared, 0, 1) as f32, // minis should be punished less but not by much + _ => extra += wtw, // idk + } + } + + combo_score + b2b + attack + clear + paranoia + extra } fn get_height_differences_score(board: &Board, weight: &Weights) -> f32 { @@ -516,24 +563,60 @@ impl Bot { adjacent_score + total_score } - pub(crate) fn get_t_slot_score(board: &Board, weight: &Weights) -> f32 { - weight.t_slot_weight.eval(board.t_slot() as f32) + fn top_of_board_score(game: &Game, board: &Board) -> f32 { + if + board.get_max_height() > MAX_PLACE_HEIGHT || + (board.get_max_height() + game.game_data.garbage_in_queue > MAX_PLACE_HEIGHT && game.game_data.combo == 0) + { + return 10000000.0 + } + 0.0 + } + + pub(crate) fn get_t_slot_score(game: &Game, board: &Board, weight: &Weights, panic: bool) -> f32 { + if game.nearest_tpiece() == 0 { + return 10.0; + } + if panic { + return weight.panic_t_slot_weight.eval(board.t_slot() as f32) * (1.0 - (game.nearest_tpiece() as f32 / MIN_QUEUE_LENGTH as f32)); + } + weight.t_slot_weight.eval(board.t_slot() as f32) * (1.0 - (game.nearest_tpiece() as f32 / MIN_QUEUE_LENGTH as f32)) } - fn get_height_score(board: &Board, weight: &Weights) -> f32 { + fn get_height_score(board: &Board, weight: &Weights, panic: bool) -> f32 { let total_height = board.get_max_height(); + if panic { + return weight.panic_height_weight.eval(total_height as f32); + } weight.height_weight.eval(total_height as f32) } - fn get_holes_and_cell_covered_score(board: &Board, weight: &Weights) -> f32 { + fn get_holes_and_cell_covered_score(board: &Board, weight: &Weights, panic: bool) -> f32 { let mut out = 0.0; let (holes_t, holes_w, covered) = board.holes_cell_covered(); - out += weight.num_hole_total_weight.eval(holes_t as f32); + if panic { + out += weight.panic_num_hole_total_weight.eval(holes_t as f32); + } else { + out += weight.num_hole_total_weight.eval(holes_t as f32); + } out += weight.num_hole_weighted_weight.eval(holes_w as f32); out += weight.cell_covered_weight.eval(covered as f32); + out += board.horizontal_holes_weighted(weight); out } + + pub fn addgarbage(&mut self, col: usize, amnt: usize) { + self.game.board.add_garbage(col,amnt) + } + + pub fn addtoboard(&mut self, row: usize, col: usize) { + self.game.board.add(row, col) + } + + pub fn removefromboard(&mut self, row: usize, col: usize) { + self.game.board.remove(row, col) + } } diff --git a/tetris/src/communications.rs b/tetris/src/communications.rs index d5933e4..2251ab1 100644 --- a/tetris/src/communications.rs +++ b/tetris/src/communications.rs @@ -112,11 +112,6 @@ async fn handle_connection(peer: SocketAddr, stream: TcpStream) -> Result<()> { }, "stop" => eprintln!("stop game"), "start" => { - let mut i_hate_osk: usize = 21; // 0+1+2+3+4+5+6 = 21 - for i in 0..6 { - i_hate_osk -= bot.get_game().piece_queue.peek_index(i) - } - bot.get_game_mut().set_active_piece(Piece::new(i_hate_osk)); ws_sender.send(Message::Text(serde_json::to_string(&json!(bot.make_suggest_move())).unwrap())).await? }, other => eprintln!("unexpected packet of type {}", other), diff --git a/tetris/src/constants.rs b/tetris/src/constants.rs index ac15ef6..6031583 100644 --- a/tetris/src/constants.rs +++ b/tetris/src/constants.rs @@ -37,6 +37,8 @@ pub mod board_constants { pub const ZERO_ONE: usize = 0x5555555555; pub const ONE_ZERO: usize = 0xAAAAAAAAAA; + + pub const CONSOLE_DISPLAY_STATS: bool = true; } pub mod piece_constants { @@ -83,7 +85,7 @@ pub mod versus_constants { TT, } - #[derive(Debug, PartialEq)] + #[derive(Copy, Clone, Debug, PartialEq)] pub enum TSpinType { None, Full, @@ -92,12 +94,13 @@ pub mod versus_constants { } pub mod queue_constants { - pub const MIN_QUEUE_LENGTH: usize = 6; + pub const MIN_QUEUE_LENGTH: usize = 13; // lehmer RNG (MINSTD) pub const MULTIPLIER: usize = 16807; pub const MODULUS: usize = 2147483647; + pub const CONSOLE_DISPLAY_QUEUE: bool = true; } pub mod bot_constants { @@ -162,6 +165,9 @@ pub mod bot_constants { Game::active_180, // Game::active_drop, ]; + + pub const MOVEPLACEMENTSCORE: usize = 7; + pub const PANICBURST: usize = 0; // test and will likely not be the function used later } pub mod rotation { @@ -432,3 +438,10 @@ pub mod offset { ], ]; } + +pub mod localbotgameplay { + pub const ALLOWLOCALGAMEPLAY: bool = true; + pub const LOCALGAMEPLAYFILEPATH: &str = r".\src\localdata.txt"; + pub const BOTNUM: usize = 0; + pub const BOTNUM2: usize = 1; +} \ No newline at end of file diff --git a/tetris/src/game.rs b/tetris/src/game.rs index bdea441..6ed1579 100644 --- a/tetris/src/game.rs +++ b/tetris/src/game.rs @@ -4,13 +4,20 @@ use crate::board::Board; use crate::constants::piece_constants::{NUM_ROTATE_STATES, RELATIVE_CORNERS}; use crate::constants::types::{PieceType, RotationDirection}; use crate::constants::versus_constants::*; +use crate::constants::queue_constants::*; use crate::piece::Piece; use crate::point_vector::PointVector; -use crate::queue::{piece_type_to_string, BagType, PieceQueue}; +use crate::queue::{piece_type_to_string, piece_type_to_string_colored, BagType, PieceQueue}; use crate::versus::*; use game_rules_and_data::*; use std::fmt::{Display, Formatter}; use crate::game::game_rules_and_data::SpinBonus::TSpin; +use colored::*; +use crate::constants::localbotgameplay::*; +use std::fs; +use crate::constants::board_constants::MAX_PLACE_HEIGHT; +use crate::constants::queue_constants::MIN_QUEUE_LENGTH; +use num::clamp; #[derive(Default, Clone)] pub struct Game { @@ -25,11 +32,13 @@ pub struct Game { impl Display for Game { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Queue: {}", self.piece_queue)?; - if let Some(hold) = self.hold_piece { - write!(f, "Hold: {}\n", piece_type_to_string(hold))?; - } else { - write!(f, "Hold: None\n")?; + if CONSOLE_DISPLAY_QUEUE { + write!(f, "Queue: {}", self.piece_queue)?; + if let Some(hold) = self.hold_piece { + write!(f, "Hold: {}\n", piece_type_to_string_colored(hold))?; + } else { + write!(f, "Hold: None\n")?; + } } write!(f, "{}", self.board.display_with_active(&self.active_piece))?; @@ -51,11 +60,14 @@ impl Game { } pub fn from_rules(seed: Option, game_rules: GameRules) -> Self { - Self { + let mut out = Self { piece_queue: PieceQueue::new(seed), game_rules, ..Default::default() - } + }; + + out.active_piece = out.piece_queue.next(); + out } // piece getters and setters pub fn get_active_piece(&self) -> &Piece { @@ -112,6 +124,7 @@ impl Game { pub fn active_drop(&mut self) -> bool { let out = self.active_down(); + if out { self.active_piece.set_kick(999)} while out && self.active_down() {} out } @@ -275,18 +288,69 @@ impl Game { self.board.set_piece(&self.active_piece); self.update(); + self.game_data.last_placed_piece = self.active_piece; self.active_piece = self.piece_queue.next(); true } + pub fn nearest_tpiece(&self) -> usize { + if self.hold_piece == Some(6) || self.active_piece.get_type() == 6 { + return 0; + } + self.piece_queue.nearest_tpiece() + } + + pub fn nearest_ipiece(&self) -> usize { + if self.hold_piece == Some(4) || self.active_piece.get_type() == 4 { + return 0; + } + self.piece_queue.nearest_ipiece() + } + + pub fn should_panic(&self) -> bool { + if + self.board.get_max_height() + self.game_data.garbage_in_queue > MAX_PLACE_HEIGHT / 2 && !self.game_data.panic || + self.game_data.combo > 0 && self.game_data.panic || + self.board.get_max_height() > MAX_PLACE_HEIGHT / 2 && self.game_data.panic + { + return true; + } + false + } + + pub fn get_paranoia(&self) -> f32 { + if self.should_panic() { + return 0.0; + } + + let (holes_count_total, holes_count_weighted, _) = self.board.holes_cell_covered(); + + (clamp(2.0 * self.board.get_max_height() as f32 / MAX_PLACE_HEIGHT as f32, 0.1, 1.0) * self.nearest_ipiece() as f32) + + (holes_count_total as f32 / 20.0 + holes_count_weighted as f32 / 8.0) + + (self.nearest_ipiece() as f32 / 4.0) + } + + pub fn get_garbage_in_queue(&self) -> usize { + let mut garbage = 0; + if ALLOWLOCALGAMEPLAY { + garbage = fs::read_to_string(LOCALGAMEPLAYFILEPATH).expect("e").chars().nth(BOTNUM).expect("e").to_string().parse::().unwrap(); + } + garbage + } + + pub fn update_garbage_amount(&mut self) { + self.game_data.garbage_in_queue = self.get_garbage_in_queue(); + self.game_data.total_garbage_recieved += self.game_data.garbage_in_queue; // will die in edge cases with garbage being sent during cancellation combos but im too lazy to accurately track this + self.game_data.panic = self.should_panic(); + } + pub fn update(&mut self) { let t_spin_type = Game::get_t_spin_type(&self.active_piece, &self.board); let lines_cleared = self.board.clear_lines(); let attack_type = attack_type(t_spin_type, lines_cleared); - self.game_data - .update(lines_cleared, attack_type, self.board.all_clear()); + self.game_data.update(lines_cleared, attack_type, t_spin_type, self.board.all_clear()); } } @@ -305,37 +369,46 @@ pub mod game_rules_and_data { pub pieces_placed: usize, pub lines_cleared: usize, pub lines_sent: u16, - pub last_sent: u8, + pub last_sent: u16, pub last_cleared: usize, + pub last_placed_piece: Piece, pub t_spin: bool, + pub t_spin_type: u16, pub game_over: bool, + + pub panic: bool, + pub garbage_in_queue: usize, + pub total_garbage_recieved: usize, } impl GameData { - pub fn update(&mut self, lines_cleared: usize, attack: AttackType, all_clear: bool) { + pub fn update(&mut self, lines_cleared: usize, attack: AttackType, t_spin_type: TSpinType, all_clear: bool) { self.pieces_placed += 1; + self.t_spin_type = t_spin_type as u16; if lines_cleared == 0 { self.combo = 0; self.all_clear = false; self.last_cleared = 0; self.last_sent = 0; + self.t_spin = false; return; } self.lines_cleared += lines_cleared; self.last_cleared = lines_cleared; - if attack == TD{ + if self.t_spin_type > 0 { self.t_spin = true; } // update lines sent before adding b2b/combo - let lines_sent = calc_damage(self, attack, lines_cleared); + let mut lines_sent = calc_damage(self, attack, lines_cleared); + if all_clear { lines_sent += 10; } self.lines_sent += lines_sent as u16; - self.last_sent = lines_sent as u8; + self.last_sent = lines_sent as u16; let b2b = BACK_TO_BACK_TYPES.contains(&attack); if b2b { self.b2b += 1; diff --git a/tetris/src/localdata.txt b/tetris/src/localdata.txt new file mode 100644 index 0000000..aa2f0b2 --- /dev/null +++ b/tetris/src/localdata.txt @@ -0,0 +1 @@ +09 \ No newline at end of file diff --git a/tetris/src/main.rs b/tetris/src/main.rs index fc3439d..a1baecb 100644 --- a/tetris/src/main.rs +++ b/tetris/src/main.rs @@ -23,16 +23,24 @@ use std::collections::VecDeque; use crate::board::Board; use crate::constants::types::Dependencies; use crate::constants::versus_constants::AttackType::T; +use crate::constants::localbotgameplay::*; +use crate::constants::board_constants::CONSOLE_DISPLAY_STATS; use crate::game::Game; use crate::piece::Piece; use crate::weight::Weights; use crate::point_vector::Point; use crate::opener::*; +use colored::Colorize; +use std::fs; +use std::string::*; fn main() { // bot_play(); - tetrio_play(); + bot_play_local(); + // tetrio_play(); + // test_tspinkicks(); + // test_cheese(); // more_test(); // dt_test(); } @@ -63,7 +71,89 @@ fn bot_play() { thread::sleep(time::Duration::from_millis(0)); // println!("{}", bot.get_game()); println!("{}", bot.get_game()); + println!("{} milliseconds to move", format!("{}", now.elapsed().as_micros() / 1000).green()) + } + println!( + "Making {} moves took {} microseconds on average", + bot.get_game().game_data.pieces_placed, + time / (bot.get_game().game_data.pieces_placed as u128) + ); + println!("{}", bot.get_game()); + thread::sleep(time::Duration::from_millis(10000)); +} + +fn bot_play_local() { + let mut bot = Bot::default(); + println!("{}", bot.get_game().board.get_arr().len()); + + let mut time = 0; + while !bot.get_game().get_game_over() && bot.get_game().game_data.pieces_placed < 10000 { + let now = time::Instant::now(); + bot.get_game_mut().update_garbage_amount(); + bot.make_move(); + time += now.elapsed().as_micros(); + + if ALLOWLOCALGAMEPLAY && bot.get_game().game_data.combo == 0 { + let mut commsfile = fs::read_to_string(LOCALGAMEPLAYFILEPATH).expect("e"); + let garbage = commsfile.chars().nth(BOTNUM).expect("e").to_string().parse::().unwrap(); + bot.addgarbage((time % 10).try_into().unwrap(), garbage); + commsfile.replace_range(BOTNUM..BOTNUM+1, "0"); + let _ = fs::write(LOCALGAMEPLAYFILEPATH, commsfile); + } + + thread::sleep(time::Duration::from_millis(0)); + // println!("{}", bot.get_game()); + println!("{}", bot.get_game()); + if CONSOLE_DISPLAY_STATS { + println!("{} milliseconds to move", format!("{}", now.elapsed().as_micros() / 1000).green()); + println!("{} pps, {} apm", format!("{}", (bot.get_game().game_data.pieces_placed as f64 / (time as f64 / 1000000000.0)).round() / 1000.0).green(), format!("{}", (60.0 * bot.get_game().game_data.lines_sent as f64 / (time as f64 / 1000000000.0)).round() / 1000.0).green()); + println!("{} lines sent / {} pieces placed = {} app", format!("{}", bot.get_game().game_data.lines_sent).green(), format!("{}", bot.get_game().game_data.pieces_placed).green(), format!("{}", (bot.get_game().game_data.lines_sent as f64) / (bot.get_game().game_data.pieces_placed as f64)).green()); + println!("{} b2b, {} combo", format!("{}", bot.get_game().game_data.b2b).green(), format!("{}", bot.get_game().game_data.combo).green()); + } + } + println!( + "Making {} moves took {} microseconds on average", + bot.get_game().game_data.pieces_placed, + time / (bot.get_game().game_data.pieces_placed as u128) + ); + println!("{}", bot.get_game()); + thread::sleep(time::Duration::from_millis(100000)); +} + +fn test_tspinkicks() { + let mut bot = Bot::default(); + println!("{}", bot.get_game().board.get_arr().len()); + + bot.addgarbage(3,3); + bot.removefromboard(1,2); + //bot.removefromboard(0,2); + bot.addtoboard(3,4); + bot.addtoboard(4,4); + bot.addtoboard(4,3); + println!("{}", bot.get_game()); + + let mut time = 0; + while !bot.get_game().get_game_over() && bot.get_game().game_data.pieces_placed < 10000 { + let now = time::Instant::now(); + bot.make_move(); + time += now.elapsed().as_micros(); + + if ALLOWLOCALGAMEPLAY && bot.get_game().game_data.combo == 0 { + let mut commsfile = fs::read_to_string(LOCALGAMEPLAYFILEPATH).expect("e"); + let garbage = commsfile.chars().nth(BOTNUM).expect("e").to_string().parse::().unwrap(); + bot.addgarbage((time % 10).try_into().unwrap(), garbage); + commsfile.replace_range(BOTNUM..BOTNUM+1, "0"); + let _ = fs::write(LOCALGAMEPLAYFILEPATH, commsfile); + } + + thread::sleep(time::Duration::from_millis(0)); + // println!("{}", bot.get_game()); + println!("{}", bot.get_game()); + println!("{}", bot.get_game().game_data.t_spin_type); + println!("{} milliseconds to move", format!("{}", now.elapsed().as_micros() / 1000).green()); + println!("{} lines sent / {} pieces placed = {} app", format!("{}", bot.get_game().game_data.lines_sent).green(), format!("{}", bot.get_game().game_data.pieces_placed).green(), format!("{}", (bot.get_game().game_data.lines_sent as f64) / (bot.get_game().game_data.pieces_placed as f64)).green()); + println!("{} b2b, {} combo", format!("{}", bot.get_game().game_data.b2b).green(), format!("{}", bot.get_game().game_data.combo).green()) } println!( "Making {} moves took {} microseconds on average", @@ -71,45 +161,37 @@ fn bot_play() { time / (bot.get_game().game_data.pieces_placed as u128) ); println!("{}", bot.get_game()); + thread::sleep(time::Duration::from_millis(100000)); } -// fn bot_play() { -// let mut bot = Bot::default(); -// let mut game = bot.get_game_mut(); -// game.set_active_piece(Piece::new(6)); -// println!("{}", game); -// game.board.add(0,0); -// game.board.add(1, 0); -// game.board.add(2, 0); -// game.board.add(0, 1); -// game.board.add(0, 3); -// game.board.add(2, 3); -// game.board.add(0, 4); -// game.board.add(1, 4); -// game.board.add(2, 4); -// game.board.add(0, 5); -// game.board.add(1, 5); -// game.board.add(2, 5); -// game.board.add(0, 6); -// game.board.add(1, 6); -// game.board.add(2, 6); -// game.board.add(0, 7); -// game.board.add(1, 7); -// game.board.add(2, 7); -// game.board.add(0, 8); -// game.board.add(1, 8); -// game.board.add(2, 8); -// game.board.add(0, 9); -// game.board.add(1, 9); -// game.board.add(2, 9); -// println!("{}", game); -// let placements = Bot::move_placement_score_1d(game, &Default::default()).1; -// for placement in placements{ -// game.board.add_list(placement.abs_locations().unwrap()); -// println!("{}", game); -// game.board.remove_list(placement.abs_locations().unwrap()); -// } -// } +fn test_cheese() { + let mut bot = Bot::default(); + println!("{}", bot.get_game().board.get_arr().len()); + + println!("{}", bot.get_game()); + + let mut time = 0; + while !bot.get_game().get_game_over() && bot.get_game().game_data.pieces_placed < 10000 { + + let now = time::Instant::now(); + bot.make_move(); + time += now.elapsed().as_micros(); + if bot.get_game().game_data.pieces_placed % 3 == 0 { bot.addgarbage((time % 10).try_into().unwrap(), 1); } // cheese timer in zen mode be like + + thread::sleep(time::Duration::from_millis(0)); + // println!("{}", bot.get_game()); + println!("{}", bot.get_game()); + println!("{} milliseconds to move", format!("{}", now.elapsed().as_micros() / 1000).green()); + println!("{} pieces placed", format!("{}", bot.get_game().game_data.pieces_placed).green()) + } + println!( + "Making {} moves took {} microseconds on average", + bot.get_game().game_data.pieces_placed, + time / (bot.get_game().game_data.pieces_placed as u128) + ); + println!("{}", bot.get_game()); + thread::sleep(time::Duration::from_millis(10000)); +} fn dt_test() { let mut bot = Bot::default(); diff --git a/tetris/src/players.rs b/tetris/src/players.rs index 1752d54..cc5bcfb 100644 --- a/tetris/src/players.rs +++ b/tetris/src/players.rs @@ -4,6 +4,11 @@ use crate::communications::Suggestion; use crate::constants::bot_constants::*; use crate::constants::types::*; use crate::game::Game; +use num::clamp; +use std::fs; +use std::string::*; +use crate::constants::localbotgameplay::*; +use std::{thread, time}; pub trait Player { fn get_game(&self) -> &Game; @@ -17,6 +22,23 @@ pub trait Player { let action = self.get_next_move(); // println!("{:?}", action); do_move_list(self.get_game_mut(), action); + + // for local gameplay in cmd + if ALLOWLOCALGAMEPLAY { + let mut commsfile = fs::read_to_string(LOCALGAMEPLAYFILEPATH).expect("e"); + let mut garbage = commsfile.chars().nth(BOTNUM).expect("e").to_string().parse::().unwrap(); + let clamped_lines_sent = clamp(clamp(self.get_game().game_data.last_sent as i16 - garbage, 0, 9) + commsfile.chars().nth(BOTNUM2).expect("e").to_string().parse::().unwrap(), 0, 9); + garbage = clamp(garbage - self.get_game().game_data.last_sent as i16, 0, 9); + commsfile.replace_range(BOTNUM..BOTNUM+1, &garbage.to_string()); + commsfile.replace_range(BOTNUM2..BOTNUM2+1, &clamped_lines_sent.to_string()); + let _ = fs::write(LOCALGAMEPLAYFILEPATH, commsfile); + + // println!("{}", &garbage.to_string()); + // println!("{}", &clamped_lines_sent.to_string()); + } + + // println!("{}", clamped_lines_sent) + // println!("{}", self.get_game().game_data.last_sent); true } @@ -57,6 +79,8 @@ pub trait Player { pub fn do_move_list(game: &mut Game, commands: CommandList) { for command in commands { do_command(game, command); + // println!("{}", game); + // thread::sleep(time::Duration::from_millis(2)); } } diff --git a/tetris/src/queue.rs b/tetris/src/queue.rs index a5d1c8d..a491a90 100644 --- a/tetris/src/queue.rs +++ b/tetris/src/queue.rs @@ -8,6 +8,7 @@ use std::collections::VecDeque; use std::fmt::{Display, Formatter}; use std::str::FromStr; use std::string::ParseError; +use colored::*; #[derive(Default, Clone)] pub struct PieceQueue { @@ -18,14 +19,10 @@ pub struct PieceQueue { impl PieceQueue { pub fn new(optional_seed: Option) -> Self { - let mut out = Self { + Self { seed: optional_seed.unwrap_or_else(|| rand::thread_rng().gen_range(0..MODULUS - 1)), ..Default::default() - }; - - out.next_bag(); - out.next(); - out + } } pub fn new_alt_randomizer(optional_seed: Option, randomizer: BagType) -> Self { @@ -59,7 +56,7 @@ impl PieceQueue { } pub fn next(&mut self) -> Piece { - if self.queue.len() <= MIN_QUEUE_LENGTH { + while self.queue.len() <= MIN_QUEUE_LENGTH { self.next_bag(); } @@ -122,6 +119,24 @@ impl PieceQueue { } arr } + + pub fn nearest_tpiece(&self) -> usize { + for piece in 0..MIN_QUEUE_LENGTH { + if self.queue[piece] == 6 { + return piece + 1; + } + } + return MIN_QUEUE_LENGTH + 1; + } + + pub fn nearest_ipiece(&self) -> usize { + for piece in 0..MIN_QUEUE_LENGTH { + if self.queue[piece] == 4 { + return piece + 1; + } + } + return MIN_QUEUE_LENGTH + 1; + } } pub fn piece_type_to_string(piece: PieceType) -> String { @@ -129,10 +144,20 @@ pub fn piece_type_to_string(piece: PieceType) -> String { arr[piece].to_owned() } +pub fn piece_type_to_string_colored(piece: PieceType) -> ColoredString { + let arr = ["Z".red(), "L".truecolor(255,128,0), "O".bright_yellow(), "S".green(), "I".bright_blue(), "J".blue(), "T".bright_purple()]; + arr[piece].to_owned() +} + +pub fn piece_type_to_block_colored(piece: PieceType) -> ColoredString { + let arr = ["■".red(), "■".truecolor(255,128,0), "■".bright_yellow(), "■".green(), "■".bright_blue(), "■".blue(), "■".bright_purple()]; + arr[piece].to_owned() +} + impl Display for PieceQueue { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { for piece in 0..MIN_QUEUE_LENGTH { - write!(f, "{} ", piece_type_to_string(self.queue[piece]))?; + write!(f, "{} ", piece_type_to_string_colored(self.queue[piece]))?; } Ok(()) } diff --git a/tetris/src/weight.rs b/tetris/src/weight.rs index 4e817da..8617d50 100644 --- a/tetris/src/weight.rs +++ b/tetris/src/weight.rs @@ -8,30 +8,68 @@ pub struct Weights { pub total_height_difference_weight: Polynomial, pub num_hole_total_weight: Polynomial, pub num_hole_weighted_weight: Polynomial, + pub holes_per_row_weighted_weight: Polynomial, pub cell_covered_weight: Polynomial, - pub t_slot_weight: Polynomial, + pub b2b_weight: Polynomial, pub combo_weight: Polynomial, pub damage_weight: Polynomial, pub clear_weight: Polynomial, + + pub waste_t_weight: f32, + pub tspin_reward: f32, + pub tspin_reward_expo: f32, + pub paranoia_weight: f32, + + pub panic_height_weight: Polynomial, + pub panic_num_hole_total_weight: Polynomial, + pub panic_t_slot_weight: Polynomial, + pub panic_b2b_weight: Polynomial, + pub panic_combo_weight: Polynomial, + pub panic_damage_weight: Polynomial, + pub panic_waste_t_weight: f32, + pub panic_tspin_reward: f32, + pub panic_tspin_reward_expo: f32, } impl Default for Weights { fn default() -> Self { Self { - height_weight: Polynomial::new(vec![0.0, -10.0, 5.0]), + // Default + + height_weight: Polynomial::new(vec![0.0, -20.0, 0.2, 0.4]), adjacent_height_differences_weight: Polynomial::new(vec![0.0, 3.0, 2.0]), total_height_difference_weight: Polynomial::new(vec![0.0, 0.0, 0.0]), - num_hole_total_weight: Polynomial::new(vec![0.0, 30.0, 5.0]), - num_hole_weighted_weight: Polynomial::new(vec![0.0, 10.0, 3.0]), - cell_covered_weight: Polynomial::new(vec![0.0, 5.0, 0.0]), - - t_slot_weight: Polynomial::new(vec![0.0, -150.0, 50.0]), - b2b_weight: Polynomial::new(vec![0.0, -15.0]), - combo_weight: Polynomial::new(vec![0.0, 8.0, -4.0]), - damage_weight: Polynomial::new(vec![0.0, 28.0, -8.0]), - clear_weight: Polynomial::new(vec![0.0, 49.0, -7.0]), + num_hole_total_weight: Polynomial::new(vec![0.0, 4.0, 1.0]), + num_hole_weighted_weight: Polynomial::new(vec![0.0, 10.0, 2.0]), + holes_per_row_weighted_weight: Polynomial::new(vec![0.0, -100.0, 100.0]), + cell_covered_weight: Polynomial::new(vec![0.0, 4.0]), + t_slot_weight: Polynomial::new(vec![0.0, -800.0, 160.0]), + + b2b_weight: Polynomial::new(vec![0.0, -60.0, -2.0]), + combo_weight: Polynomial::new(vec![0.0, 60.0, -2.0, -1.0]), + damage_weight: Polynomial::new(vec![0.0, 30.0, -8.0, -1.5]), + clear_weight: Polynomial::new(vec![0.0, -5.0]), + + waste_t_weight: 600.0, + tspin_reward: 100.0, + tspin_reward_expo: 4.0, + paranoia_weight: 40.0, + + // Panic + + panic_height_weight: Polynomial::new(vec![0.0, 80.0, 1.0, 1.0]), + panic_num_hole_total_weight: Polynomial::new(vec![0.0, 100.0, 3.0]), + panic_t_slot_weight: Polynomial::new(vec![0.0, -40.0, 20.0]), + + panic_b2b_weight: Polynomial::new(vec![0.0]), + panic_combo_weight: Polynomial::new(vec![0.0, 20.0, -16.0, -2.5]), + panic_damage_weight: Polynomial::new(vec![0.0, 40.0, -12.0, -2.0]), + + panic_waste_t_weight: 10.0, + panic_tspin_reward: 400.0, + panic_tspin_reward_expo: 1.0, } } -} +} \ No newline at end of file