diff --git a/.gitignore b/.gitignore index ea8c4bf..55a3905 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /target +/flamegraph.svg +/perf.data +/perf.data.old \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 88ad7b9..d516d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,26 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "alloc_counter" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8c3a0a472b3a556e269be64dc65a5c013ba85e940d089367eb8c88f3fdfda9" -dependencies = [ - "alloc_counter_macro", - "pin-utils", -] - -[[package]] -name = "alloc_counter_macro" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a52f81f9add01deacdc1fcb05ba09523a8faefdec6c3f69cb752b9fa9c22e5a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "cfg-if" version = "0.1.10" @@ -58,25 +37,39 @@ checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" name = "mercury" version = "0.1.0" dependencies = [ - "alloc_counter", "coz", + "modular-bitfield", "rand", - "smallvec", "tinyvec", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "once_cell" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -103,9 +96,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12" +checksum = "c24fcd450d3fa2b592732565aa4f17a27a61c65ece4726353e000939b0edee34" dependencies = [ "libc", "rand_chacha", @@ -142,16 +135,16 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.6.0" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 70614b5..a50eb0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,16 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8.0" +rand = "0.8.1" coz = "0.1" -smallvec = "1.6.0" tinyvec = "1.1.0" +modular-bitfield = "0.11.2" + #jemallocator = "0.3.2" #mimalloc = { version = "0.1.22", default-features = false } -alloc_counter = "0.0.4" +#alloc_counter = "0.0.4" [profile.release] debug = 1 diff --git a/shell.nix b/shell.nix index a51c134..3ab8928 100644 --- a/shell.nix +++ b/shell.nix @@ -1,7 +1,7 @@ let moz_overlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz); nixpkgs = import { overlays = [ moz_overlay ]; }; - rustNightlyChannel = (nixpkgs.rustChannelOf { date = "2020-03-19"; channel = "nightly"; }).rust.override { + rustNightlyChannel = (nixpkgs.rustChannelOf { date = "2021-01-01"; channel = "nightly"; }).rust.override { extensions = [ "rust-src" "rls-preview" @@ -22,7 +22,7 @@ with nixpkgs; stdenv.mkDerivation { name = "moz_overlay_shell"; buildInputs = [ - rustStableChannel + rustNightlyChannel (vscode-with-extensions.override { vscodeExtensions = with vscode-extensions; [ bbenoist.Nix diff --git a/src/azul.rs b/src/azul.rs index 4768e21..8c98060 100644 --- a/src/azul.rs +++ b/src/azul.rs @@ -1,10 +1,27 @@ use std::ops::{Deref, DerefMut}; use rand::prelude::*; -use smallvec::{SmallVec, smallvec}; +use rand::distributions::WeightedIndex; +//use smallvec::{SmallVec, smallvec}; -use alloc_counter::{AllocCounterSystem, no_alloc}; -#[global_allocator] -static A: AllocCounterSystem = AllocCounterSystem; +//use alloc_counter::{AllocCounterSystem, no_alloc}; +//#[global_allocator] +//static A: AllocCounterSystem = AllocCounterSystem; + +pub fn size_of_stuff() { + println!("size of azul game: {}", std::mem::size_of::()); + println!("size of azul tile: {}", std::mem::size_of::()); + println!("size of azul bag: {}", std::mem::size_of::()); + println!("size of azul market: {}", std::mem::size_of::()); + println!("size of azul factories: {}", std::mem::size_of::>()); + println!("size of azul factory array: {}", std::mem::size_of::<[Factory; 9]>()); + println!("size of azul factory: {}", std::mem::size_of::()); + println!("size of azul boards: {}", std::mem::size_of::>()); + println!("size of azul board array: {}", std::mem::size_of::<[Board; 4]>()); + println!("size of azul board: {}", std::mem::size_of::()); + + println!("size of azul option tile: {}", std::mem::size_of::>()); + +} #[derive(Debug, Clone, Copy, PartialEq)] pub enum Tile { @@ -193,7 +210,7 @@ impl From> for Bag { #[derive(Default, Debug, Copy)] struct Factory (tinyvec::ArrayVec<[Tile; 4]>); impl Clone for Factory { - #[no_alloc] + //#[no_alloc] fn clone(&self) -> Self { Factory(self.0.clone()) } @@ -345,25 +362,26 @@ impl Board { } } +//#[repr(align(16))] #[derive(Debug, Clone, Copy)] pub struct Game { turn: u32, - player: usize, + player: u8, box_top: Bag, bag: Bag, market: Market, - factories: tinyvec::ArrayVec<[Factory; 9]>, // TODO set to 9? - boards: tinyvec::ArrayVec<[Board; 4]> // TODO set to 4? + factories: tinyvec::ArrayVec<[Factory; 5]>, // TODO set to 9? + boards: tinyvec::ArrayVec<[Board; 2]> // TODO set to 4? } impl Game { pub fn new(players: u8) -> Result { let n_factories = get_n_factories(players)?; - let mut factories = tinyvec::ArrayVec::<[Factory; 9]>::new(); + let mut factories = tinyvec::ArrayVec::<[Factory; 5]>::new(); for _ in 0..n_factories { factories.push(Factory::default()) } - let mut boards = tinyvec::ArrayVec::<[Board; 4]>::new(); + let mut boards = tinyvec::ArrayVec::<[Board; 2]>::new(); for _ in 0..players { boards.push(Board::default()); } @@ -430,9 +448,9 @@ impl Game { } Ok(()) } - #[no_alloc(forbid)] + // #[no_alloc(forbid)] pub fn do_move(&mut self, game_move: GameMove) -> Result<(), &'static str> { - let board = &mut self.boards[self.player]; + let board = &mut self.boards[self.player as usize]; match game_move { GameMove(_, Tile::Start, _) => return Err("You can't take the start tile specifically"), GameMove(0, _, 0) => { @@ -487,7 +505,7 @@ impl Game { } }, GameMove(1..=9, _, _) => { - let board = &mut self.boards[self.player]; + let board = &mut self.boards[self.player as usize]; if game_move.0 > self.factories.len() { return Err("That factory is out of bounds"); } @@ -561,7 +579,7 @@ impl Game { self.player = (self.player + 1) % self.boards.len(); } */ - self.player = (self.player + 1) % self.boards.len(); + self.player = (self.player + 1) % self.boards.len() as u8; self.turn += 1; Ok(()) } @@ -587,7 +605,7 @@ pub fn complicated() -> Result { #[test] fn bag() { - let game = Game::new(2, StdRng::seed_from_u64(123)).unwrap(); + let game = Game::new(2).unwrap(); let bag = game.bag; assert_eq!(bag.len(), 100); @@ -621,4 +639,522 @@ fn game_move_iter() { assert_eq!(i.into_iter().next().unwrap(), GameMove(1, Tile::Blue, 1)); assert_eq!(i.into_iter().count(), 5) +} + +#[test] +fn sizes() { + println!("size of azul game: {}", std::mem::size_of::()); +} + + +use modular_bitfield::prelude::*; + +#[derive(BitfieldSpecifier)] +#[bits = 3] +#[derive(Debug, Clone, Copy, PartialEq)] +enum Tile2 { + Blue, + Yellow, + Red, + Black, + Teal, + Start, + None +} + +impl Tile2 { + fn is_none(&self) -> bool { + *self == Tile2::None + } + fn is_some(&self) -> bool { + !self.is_none() + } +} + +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +enum Player2 { + One, + Two +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Bag2 { + blue: B5, + yellow: B5, + red: B5, + black: B5, + teal: B5, +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Factory2 { + one: Tile2, + two: Tile2, + three: Tile2, + four: Tile2 +} +impl Factory2 { + fn is_empty(&self) -> bool { + self.one().is_none() + && self.two().is_none() + && self.three().is_none() + && self.four().is_none() + } + fn is_full(&self) -> bool { + self.one().is_some() + && self.two().is_some() + && self.three().is_some() + && self.four().is_some() + } + fn add_to_first(&mut self, tile: Tile2) -> Result<(), Tile2> { + if self.one().is_none() { + self.set_one(tile); + return Ok(()); + } + else if self.two().is_none() { + self.set_two(tile); + return Ok(()); + } + else if self.three().is_none() { + self.set_three(tile); + return Ok(()); + } + else if self.four().is_none() { + self.set_four(tile); + return Ok(()); + } + Err(tile) + } +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Factories2 { + one: Factory2, + two: Factory2, + three: Factory2, + four: Factory2, + five: Factory2, +} +impl Factories2 { + fn is_empty(&self) -> bool { + self.one().is_empty() + && self.two().is_empty() + && self.three().is_empty() + && self.four().is_empty() + && self.five().is_empty() + } + fn add_to_firsts(&mut self, tile: Tile2) -> Result<(), Tile2>{ + if !self.one().is_full() { + self.one().add_to_first(tile); + return Ok(()) + } + else if !self.two().is_full() { + self.two().add_to_first(tile); + return Ok(()) + } + else if !self.three().is_full() { + self.three().add_to_first(tile); + return Ok(()) + } + else if !self.four().is_full() { + self.four().add_to_first(tile); + return Ok(()) + } + else if !self.five().is_full() { + self.five().add_to_first(tile); + return Ok(()) + } + Err(tile) + } +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Row2 { + one: bool, + two: bool, + three: bool, + four: bool, + five: bool +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Wall2 { + one: Row2, + two: Row2, + three: Row2, + four: Row2, + five: Row2 +} +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Market2 { + blue: B5, + yellow: B5, + red: B5, + black: B5, + teal: B5, + start: bool +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Pattern2_1 (Tile2, B1); +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Pattern2_2 (Tile2, B2); +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Pattern2_3 (Tile2, B2); +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Pattern2_4 (Tile2, B3); +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Pattern2_5 (Tile2, B3); + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Patterns2 { + one: Pattern2_1, + two: Pattern2_2, + three: Pattern2_3, + four: Pattern2_4, + five: Pattern2_5, +} + +#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +#[derive(Debug)] +struct Board2 { + score: B7, + wall: Wall2, + patterns: Patterns2, + floor: Market2 +} + +/*#[bitfield(filled = false)] +#[derive(BitfieldSpecifier)] +struct Boards2 { + one: Board2, + two: Board2 +}*/ + +#[bitfield] +#[derive(Debug, Copy, Clone)] +pub struct Game2 { + player: Player2, + box_top: Bag2, + bag: Bag2, + market: Market2, + factories: Factories2, + board_1: Board2, + board_2: Board2, + #[skip] + unused: B7 +} + +impl Game2 { + pub fn create() -> Self { + let game = Game2::new() + .with_player(Player2::One) + .with_box_top(Bag2::new() + .with_blue(0) + .with_yellow(0) + .with_red(0) + .with_black(0) + .with_teal(0) + ) + .with_bag(Bag2::new() + .with_blue(20) + .with_yellow(20) + .with_red(20) + .with_black(20) + .with_teal(20) + ) + .with_market(Market2::new() + .with_start(true) + .with_blue(0) + .with_yellow(0) + .with_red(0) + .with_black(0) + .with_teal(0) + ) + .with_factories(Factories2::new() + .with_one(Factory2::new() + .with_one(Tile2::None) + .with_two(Tile2::None) + .with_three(Tile2::None) + .with_four(Tile2::None) + ) + .with_two(Factory2::new() + .with_one(Tile2::None) + .with_two(Tile2::None) + .with_three(Tile2::None) + .with_four(Tile2::None) + ) + .with_three(Factory2::new() + .with_one(Tile2::None) + .with_two(Tile2::None) + .with_three(Tile2::None) + .with_four(Tile2::None) + ) + .with_four(Factory2::new() + .with_one(Tile2::None) + .with_two(Tile2::None) + .with_three(Tile2::None) + .with_four(Tile2::None) + ) + .with_five(Factory2::new() + .with_one(Tile2::None) + .with_two(Tile2::None) + .with_three(Tile2::None) + .with_four(Tile2::None) + ) + ) + .with_board_1(Board2::new() + .with_score(0) + .with_wall(Wall2::new() + .with_one(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_two(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_three(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_four(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_five(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + ) + .with_patterns(Patterns2::new() + .with_one(Pattern2_1::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_two(Pattern2_2::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_three(Pattern2_3::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_four(Pattern2_4::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_five(Pattern2_5::new() + .with_0(Tile2::None) + .with_1(0) + ) + ) + .with_floor(Market2::new() + .with_start(false) + .with_blue(0) + .with_yellow(0) + .with_red(0) + .with_black(0) + .with_teal(0) + ) + ) + .with_board_2(Board2::new() + .with_score(0) + .with_wall(Wall2::new() + .with_one(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_two(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_three(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_four(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + .with_five(Row2::new() + .with_one(false) + .with_two(false) + .with_three(false) + .with_four(false) + .with_five(false) + ) + ) + .with_patterns(Patterns2::new() + .with_one(Pattern2_1::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_two(Pattern2_2::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_three(Pattern2_3::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_four(Pattern2_4::new() + .with_0(Tile2::None) + .with_1(0) + ) + .with_five(Pattern2_5::new() + .with_0(Tile2::None) + .with_1(0) + ) + ) + .with_floor(Market2::new() + .with_start(false) + .with_blue(0) + .with_yellow(0) + .with_red(0) + .with_black(0) + .with_teal(0) + ) + ); + game + } + pub fn fill(&mut self, mut rng: StdRng) -> Result<(), &'static str> { + if !self.factories().is_empty() { + return Err("Factories are not empty"); + } + + for _ in 0..(4*5) { + let choices = [Tile2::Blue, Tile2::Yellow, Tile2::Red, Tile2::Black, Tile2::Teal]; + let weights = [self.bag().blue(), self.bag().yellow(), self.bag().red(), self.bag().black(), self.bag().teal()]; + let dist = WeightedIndex::new(&weights).unwrap(); + + let tile = choices[dist.sample(&mut rng)]; + + match self.set_factories(self.factories().add_to_firsts(tile)) { + Err(e) => return Err("Couldn't add tile to factory"), + Ok(_) => { + if tile == Tile2::Blue { + self.set_bag(self.bag().with_blue(self.bag().blue() - 1)); + } + else if tile == Tile2::Yellow { + self.set_bag(self.bag().with_yellow(self.bag().yellow() - 1)); + } + } + }; + + println!("{:?}", tile); + println!("{:#?}", self.bag()); + //println!("{:#?}", self.factories()); + } + + Ok(()) +/* for factory in &mut self.factories { + for _ in 0..4 { + if self.bag.len() == 0 && self.box_top.len() > 0 { + self.bag.append(&mut self.box_top); + } + else if self.bag.len() == 0 { + return Ok(()) + } + else { + let tile_i:usize = rng.gen_range(0..self.bag.len()); + let tile = self.bag.remove(tile_i); + factory.push(tile); + } + } + }; +*/ + } + /*pub fn do_move(&mut self, game_move: GameMove) -> Result<(), &'static str> { + match game_move { + () => + } + }*/ +} + +use std::time::{Instant, Duration}; + +pub fn size_of_bitfields() -> Result<(), &'static str> { + //println!("size of bitfield game: {}", std::mem::size_of::()); + + let game2 = Game2::create(); + //println!("debug: {:#?}", game2); + //println!("{:?}", game2.into_bytes()); + + let now = Instant::now(); + for _ in 0..1_000_000_000 { + let mut game2_2 = game2.clone(); + game2_2.set_player(Player2::Two); + std::hint::black_box(game2_2); + } + let game_2_time = now.elapsed().as_nanos(); + + let game1 = Game::new(2)?; + + let now = Instant::now(); + for _ in 0..1_000_000_000 { + let mut game1_2 = game1.clone(); + game1_2.turn = 1; + std::hint::black_box(game1_2); + } + let game_1_time = now.elapsed().as_nanos(); + + println!("{} | {}", game_1_time, game_2_time); + + Ok(()) + + } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 757935b..85bc14a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(test)] + mod azul; use azul::*; use rand::prelude::*; @@ -8,18 +10,19 @@ use rand::prelude::*; //#[global_allocator] //static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; - fn main() -> Result<(), &'static str> { let program = std::env::args().nth(1).expect("no program given") .parse().unwrap_or(1); + // size_of_stuff(); + return match program { 1 => { let mut rng = StdRng::seed_from_u64(42); let mut game = Game::new(2)?; game.fill(StdRng::from_rng(&mut rng).expect("rng error"))?; - println!("{:#?}", game); + //println!("{:#?}", game); game.do_move(GameMove(3, Tile::Red, 3))?; game.do_move(GameMove(1, Tile::Yellow, 2))?; @@ -30,13 +33,19 @@ fn main() -> Result<(), &'static str> { game.do_move(GameMove(5, Tile::Black, 1))?; //game.do_move(GameMove(0, Tile::Blue, 3))?; - println!("{:#?}", game); + // println!("{:#?}", game); println!("{}", count_options(game, 1, 2)); Ok(()) }, 2 => calculate_options(), + 3 => size_of_bitfields(), + 4 => { + let mut game = Game2::create(); + game.fill(StdRng::seed_from_u64(42)); + Ok(()) + }, _ => Err("Not a valid program") } }