Add initial station generator

This commit is contained in:
hal8174 2025-01-14 23:33:20 +01:00
parent 24b989b9f2
commit c4a17d1792
9 changed files with 398 additions and 37 deletions

View file

@ -0,0 +1 @@
0eNqd0MEOgjAMANB/6bkQYU7dfoUQA9iYJmyQbaiE7N8dePCg8eClWbv1Le0CbT/R6NgG0AtwN1gPulrA89U2/VqzjSHQYOjCk8mopy447rJx6AkiAtsLPUAXsUYgGzgwvYQtmc92Mi259AB/Sgjj4FPzYNc/EyhyiTCDzopdkcsY8UMs/xVV+R0U/4KnwwqmBdzZbdNXBUosUdZYpYgindItBzIJf28c4UbOb6A8lGqvlJTidBRKxPgE1KeFBQ==

View file

@ -1,4 +1,6 @@
use super::{common::PathField, Problem}; use super::{common::PathField, Problem};
use crate::blueprint::belt::{convert_to_blueprint, Beltspeed};
use crate::blueprint::BlueprintEntity;
use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map}; use crate::{belt_finding::brute_force::BruteforceBuilder, misc::Map};
use crate::{common::visualize::Visualize, prelude::*}; use crate::{common::visualize::Visualize, prelude::*};
use std::{ use std::{
@ -496,6 +498,15 @@ impl ConflictAvoidance {
} }
true true
} }
pub fn belt_blueprint(&self, speed: &Beltspeed, nextfree: &mut u32) -> Vec<BlueprintEntity> {
let mut res = Vec::new();
for path in &self.belts {
res.extend(convert_to_blueprint(&path[1..], speed, nextfree));
}
res
}
} }
impl Visualize for ConflictAvoidance { impl Visualize for ConflictAvoidance {

View file

@ -1,6 +1,7 @@
use clap::{Parser, Subcommand, ValueEnum}; use clap::{Parser, Subcommand, ValueEnum};
use factorio_blueprint::{ use factorio_blueprint::{
belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem}, belt_finding::{conflict_avoidance::ConflictAvoidance, problems, Problem},
blueprint::{Blueprint, BlueprintString},
common::visualize::Visualize, common::visualize::Visualize,
}; };
use std::{io, path::PathBuf}; use std::{io, path::PathBuf};
@ -46,6 +47,26 @@ struct Args {
problem: ProblemCase, problem: ProblemCase,
} }
fn output_blueprint(conflict: &ConflictAvoidance) {
let mut offset = 0;
let belts = conflict.belt_blueprint(
&factorio_blueprint::blueprint::belt::Beltspeed::Normal,
&mut offset,
);
let bp = BlueprintString::Blueprint(
Blueprint::builder()
.entities(belts)
.label(format!("test"))
.build(),
);
println!(
"{}",
factorio_blueprint::blueprint::encode(&serde_json::to_string(&bp).unwrap())
);
}
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
@ -67,6 +88,7 @@ fn main() {
while c.remove_conflict(None) { while c.remove_conflict(None) {
c.print_visualization(); c.print_visualization();
} }
output_blueprint(&c);
} }
Mode::ConflictStep => { Mode::ConflictStep => {
p.print_visualization(); p.print_visualization();
@ -79,6 +101,7 @@ fn main() {
let mut s = String::new(); let mut s = String::new();
let _ = io::stdin().read_line(&mut s); let _ = io::stdin().read_line(&mut s);
} }
output_blueprint(&c);
} }
} }
} }

12
src/bin/station.rs Normal file
View file

@ -0,0 +1,12 @@
use factorio_blueprint::blueprint::{station::basic_unload_station, BlueprintString};
fn main() {
let b = BlueprintString::Blueprint(basic_unload_station());
println!("{}", serde_json::to_string_pretty(&b).unwrap());
println!(
"{}",
factorio_blueprint::blueprint::encode(&serde_json::to_string(&b).unwrap())
);
}

View file

@ -10,7 +10,7 @@ pub enum Beltspeed {
} }
impl Beltspeed { impl Beltspeed {
fn string(&self) -> String { pub fn string(&self) -> String {
match self { match self {
Beltspeed::Normal => "transport-belt", Beltspeed::Normal => "transport-belt",
Beltspeed::Fast => "fast-transport-belt", Beltspeed::Fast => "fast-transport-belt",
@ -20,7 +20,7 @@ impl Beltspeed {
.to_owned() .to_owned()
} }
fn string_underground(&self) -> String { pub fn string_underground(&self) -> String {
match self { match self {
Beltspeed::Normal => "underground-belt", Beltspeed::Normal => "underground-belt",
Beltspeed::Fast => "fast-underground-belt", Beltspeed::Fast => "fast-underground-belt",
@ -29,6 +29,25 @@ impl Beltspeed {
} }
.to_owned() .to_owned()
} }
pub fn string_splitter(&self) -> String {
match self {
Beltspeed::Normal => "splitter",
Beltspeed::Fast => "fast-splitter",
Beltspeed::Express => "express-splitter",
Beltspeed::Turbo => "turbo-splitter",
}
.to_owned()
}
pub fn halve(&self) -> Self {
match self {
Beltspeed::Normal => Beltspeed::Normal,
Beltspeed::Fast => Beltspeed::Normal,
Beltspeed::Express => Beltspeed::Fast,
Beltspeed::Turbo => Beltspeed::Fast,
}
}
} }
pub fn convert_belt_to_blueprint( pub fn convert_belt_to_blueprint(

View file

@ -10,6 +10,7 @@ pub use structs::*;
pub mod balancer; pub mod balancer;
pub mod belt; pub mod belt;
pub mod station;
pub mod train; pub mod train;
pub fn decode(s: &str) -> String { pub fn decode(s: &str) -> String {

290
src/blueprint/station.rs Normal file
View file

@ -0,0 +1,290 @@
use std::sync::Arc;
use super::{belt::Beltspeed, Blueprint, BlueprintEntity, BlueprintPosition};
pub fn station_unload(
length: usize,
locomotives: usize,
unloader: &Vec<BlueprintEntity>,
beltspeed: Beltspeed,
double: bool,
output_x: f64,
output_y: f64,
) -> Blueprint {
assert!(length.is_power_of_two());
let mut e = Vec::new();
let global_x_offset = locomotives * 7;
for l in 1..=(length + locomotives) {
e.push(
BlueprintEntity::builder(
"medium-electric-pole".to_owned(),
l as u32 - 1,
BlueprintPosition::new((7 * l) as f64 + 0.5, -1.5),
)
.build(),
);
}
let offset = e.len();
for l in 0..length {
e.extend(unloader.iter().cloned().map(|mut e| {
e.position.x += (7 * l + global_x_offset) as f64;
e.entity_number += (offset + unloader.len() * l) as u32;
e
}));
}
let offset = e.len();
e.push(
BlueprintEntity::builder(
"train-stop".to_owned(),
offset as u32,
BlueprintPosition::new(1.0, -1.0),
)
.direction(12)
.build(),
);
for l in 0..((length * 7 + global_x_offset + 1) / 2) {
let offset = e.len() as u32;
e.push(
BlueprintEntity::builder(
"straight-rail".to_owned(),
offset,
BlueprintPosition::new(2.0 * l as f64 + 1.0, 1.0),
)
.direction(4)
.build(),
);
}
for i in 0..length {
let depth = (i / 2).count_ones();
for j in 0..depth {
let offset = e.len() as u32;
e.push(
BlueprintEntity::builder(
beltspeed.string(),
offset,
BlueprintPosition::new(
(7 * i + global_x_offset) as f64 + output_x,
output_y - j as f64,
),
)
.build(),
);
}
if i % 2 == 0 {
let offset = e.len() as u32;
e.push(
BlueprintEntity::builder(
beltspeed.string(),
offset,
BlueprintPosition::new(
(7 * i + global_x_offset) as f64 + output_x,
output_y - depth as f64,
),
)
.direction(12)
.build(),
);
let offset = e.len() as u32;
e.push(
BlueprintEntity::builder(
beltspeed.string_splitter(),
offset,
BlueprintPosition::new(
(7 * i + global_x_offset) as f64 + output_x - 1.0,
output_y - depth as f64 - 0.5,
),
)
.direction(12)
.build(),
);
} else {
let offset = e.len() as u32;
e.extend((0..=7).map(|j| {
BlueprintEntity::builder(
beltspeed.string(),
offset + j as u32,
BlueprintPosition::new(
(7 * i - j + global_x_offset) as f64 + output_x,
output_y - depth as f64 - 1.0,
),
)
.direction(12)
.build()
}));
let offset = e.len() as u32;
e.push(
BlueprintEntity::builder(
beltspeed.string(),
offset,
BlueprintPosition::new(
(7 * i + global_x_offset) as f64 + output_x,
output_y - depth as f64,
),
)
.build(),
);
}
}
for i in 2..(length.ilog2() as usize + 1) {
let p = 1 << i;
for j in 0..(length / p) {
let depth = j.count_ones();
let offset = e.len() as u32;
e.push(
BlueprintEntity::builder(
beltspeed.string_splitter(),
offset,
BlueprintPosition::new(
(7 * j * p + global_x_offset) as f64 - i as f64 + output_x,
output_y - i as f64 + 0.5 - depth as f64,
),
)
.direction(12)
.build(),
);
e.extend((0..(7 * p / 2)).map(|l| {
BlueprintEntity::builder(
beltspeed.string(),
offset + l as u32,
BlueprintPosition::new(
(7 * j * p + global_x_offset) as f64 - i as f64 + output_x + l as f64 + 1.0,
output_y - i as f64 - depth as f64,
),
)
.direction(12)
.build()
}))
}
}
Blueprint::builder()
.label("station".to_owned())
.entities(e)
.wires(
(0..((length + locomotives - 1) as u32))
.map(|i| [i, 5, i + 1, 5])
.collect(),
)
.build()
}
pub fn basic_unload_station() -> Blueprint {
let top = vec![
BlueprintEntity::builder(
"steel-chest".to_owned(),
0,
BlueprintPosition::new(1.5, -1.5),
)
.build(),
BlueprintEntity::builder(
"bulk-inserter".to_owned(),
1,
BlueprintPosition::new(1.5, -0.5),
)
.direction(8)
.build(),
BlueprintEntity::builder(
"fast-inserter".to_owned(),
2,
BlueprintPosition::new(2.5, -1.5),
)
.direction(12)
.build(),
BlueprintEntity::builder(
"fast-transport-belt".to_owned(),
3,
BlueprintPosition::new(3.5, -1.5),
)
.build(),
];
let bottom = vec![
BlueprintEntity::builder(
"steel-chest".to_owned(),
0,
BlueprintPosition::new(1.5, -1.5),
)
.build(),
BlueprintEntity::builder(
"bulk-inserter".to_owned(),
1,
BlueprintPosition::new(1.5, -0.5),
)
.direction(8)
.build(),
BlueprintEntity::builder(
"fast-inserter".to_owned(),
2,
BlueprintPosition::new(2.5, -1.5),
)
.direction(12)
.build(),
BlueprintEntity::builder(
"fast-transport-belt".to_owned(),
3,
BlueprintPosition::new(3.5, -1.5),
)
.build(),
];
let full = vec![
BlueprintEntity::builder(
"steel-chest".to_owned(),
0,
BlueprintPosition::new(1.5, -1.5),
)
.build(),
BlueprintEntity::builder(
"bulk-inserter".to_owned(),
1,
BlueprintPosition::new(1.5, -0.5),
)
.direction(8)
.build(),
BlueprintEntity::builder(
"fast-inserter".to_owned(),
2,
BlueprintPosition::new(2.5, -1.5),
)
.direction(12)
.build(),
BlueprintEntity::builder(
"steel-chest".to_owned(),
0,
BlueprintPosition::new(5.5, -1.5),
)
.build(),
BlueprintEntity::builder(
"bulk-inserter".to_owned(),
1,
BlueprintPosition::new(5.5, -0.5),
)
.direction(8)
.build(),
BlueprintEntity::builder(
"fast-inserter".to_owned(),
2,
BlueprintPosition::new(4.5, -1.5),
)
.direction(4)
.build(),
BlueprintEntity::builder(
"fast-transport-belt".to_owned(),
3,
BlueprintPosition::new(3.5, -1.5),
)
.build(),
];
station_unload(64, 3, &full, Beltspeed::Normal, false, 3.5, -2.5)
}

View file

@ -10,7 +10,7 @@ const fn calculate_version(major: u16, minor: u16, patch: u16, dev: u16) -> u64
((major as u64) << 48) | ((minor as u64) << 32) | ((patch as u64) << 16) | (dev as u64) ((major as u64) << 48) | ((minor as u64) << 32) | ((patch as u64) << 16) | (dev as u64)
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum BlueprintString { pub enum BlueprintString {
#[serde(rename = "blueprint_book")] #[serde(rename = "blueprint_book")]
BlueprintBook(BlueprintBook), BlueprintBook(BlueprintBook),
@ -18,7 +18,7 @@ pub enum BlueprintString {
Blueprint(Blueprint), Blueprint(Blueprint),
} }
#[derive(Serialize, Deserialize, Debug, Builder)] #[derive(Serialize, Deserialize, Debug, Clone, Builder)]
pub struct BlueprintBook { pub struct BlueprintBook {
#[builder(skip = "blueprint-book".to_owned())] #[builder(skip = "blueprint-book".to_owned())]
item: String, item: String,
@ -32,7 +32,7 @@ pub struct BlueprintBook {
version: u64, version: u64,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintBookEntry { pub struct BlueprintBookEntry {
#[serde(flatten)] #[serde(flatten)]
entry: BlueprintString, entry: BlueprintString,
@ -45,7 +45,7 @@ impl BlueprintBookEntry {
} }
} }
#[derive(Serialize, Deserialize, Debug, Builder)] #[derive(Serialize, Deserialize, Debug, Clone, Builder)]
pub struct Blueprint { pub struct Blueprint {
#[builder(skip = "blueprint".to_owned())] #[builder(skip = "blueprint".to_owned())]
item: String, item: String,
@ -75,11 +75,14 @@ pub struct Blueprint {
absolute_snapping: Option<bool>, absolute_snapping: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
position_relative_to_grid: Option<BlueprintPosition>, position_relative_to_grid: Option<BlueprintPosition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default)]
wires: Vec<[u32; 4]>,
#[builder(skip = VERSION)] #[builder(skip = VERSION)]
version: u64, version: u64,
} }
#[derive(Serialize, Deserialize, Debug, Builder)] #[derive(Serialize, Deserialize, Debug, Clone, Builder)]
pub struct BlueprintStockConnection { pub struct BlueprintStockConnection {
#[builder(start_fn)] #[builder(start_fn)]
stock: u32, stock: u32,
@ -89,14 +92,14 @@ pub struct BlueprintStockConnection {
back: Option<u32>, back: Option<u32>,
} }
#[derive(Serialize, Deserialize, Debug, Builder)] #[derive(Serialize, Deserialize, Debug, Clone, Builder)]
pub struct BlueprintEntity { pub struct BlueprintEntity {
#[builder(start_fn)] #[builder(start_fn)]
name: String, name: String,
#[builder(start_fn)] #[builder(start_fn)]
entity_number: u32, pub entity_number: u32,
#[builder(start_fn)] #[builder(start_fn)]
position: BlueprintPosition, pub position: BlueprintPosition,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
direction: Option<u8>, direction: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -159,7 +162,7 @@ pub struct BlueprintEntity {
tags: Option<serde_json::Value>, tags: Option<serde_json::Value>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintItemRequest { pub struct BlueprintItemRequest {
id: BlueprintItemID, id: BlueprintItemID,
items: BlueprintItem, items: BlueprintItem,
@ -176,18 +179,18 @@ impl BlueprintItemRequest {
} }
} }
#[derive(Serialize, Deserialize, Debug, Builder)] #[derive(Serialize, Deserialize, Debug, Clone, Builder)]
pub struct BlueprintItemID { pub struct BlueprintItemID {
#[builder(start_fn)] #[builder(start_fn)]
name: String, name: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintItem { pub struct BlueprintItem {
in_inventory: Vec<BlueprintInventoryLocation>, in_inventory: Vec<BlueprintInventoryLocation>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintInventoryLocation { pub struct BlueprintInventoryLocation {
count: u32, count: u32,
inventory: u32, inventory: u32,
@ -204,13 +207,13 @@ impl BlueprintInventoryLocation {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintInventory { pub struct BlueprintInventory {
filters: Vec<BlueprintItemFilter>, filters: Vec<BlueprintItemFilter>,
bar: u16, bar: u16,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintConnection { pub struct BlueprintConnection {
#[serde(rename = "1")] #[serde(rename = "1")]
first: Option<BlueprintConnectionPoint>, first: Option<BlueprintConnectionPoint>,
@ -218,7 +221,7 @@ pub struct BlueprintConnection {
second: Option<BlueprintConnectionPoint>, second: Option<BlueprintConnectionPoint>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintConnectionPoint { pub struct BlueprintConnectionPoint {
#[serde(default)] #[serde(default)]
red: Vec<BlueprintConnectionData>, red: Vec<BlueprintConnectionData>,
@ -226,33 +229,33 @@ pub struct BlueprintConnectionPoint {
green: Vec<BlueprintConnectionData>, green: Vec<BlueprintConnectionData>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct BlueprintConnectionData { pub struct BlueprintConnectionData {
entity_id: u64, entity_id: u64,
circuit_id: Option<u64>, circuit_id: Option<u64>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintItemFilter { pub struct BlueprintItemFilter {
name: String, name: String,
index: u32, index: u32,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintLogisticFilter { pub struct BlueprintLogisticFilter {
name: String, name: String,
index: u32, index: u32,
count: u32, count: u32,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintSpeakerParameter { pub struct BlueprintSpeakerParameter {
playback_volume: f64, playback_volume: f64,
playback_globally: bool, playback_globally: bool,
allow_polyphony: bool, allow_polyphony: bool,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintSpeakerAlertParameter { pub struct BlueprintSpeakerAlertParameter {
show_alert: bool, show_alert: bool,
show_on_map: bool, show_on_map: bool,
@ -260,17 +263,17 @@ pub struct BlueprintSpeakerAlertParameter {
alert_message: String, alert_message: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintSignalID { pub struct BlueprintSignalID {
name: String, name: String,
#[serde(rename = "type")] #[serde(rename = "type")]
signal_type: String, signal_type: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct BlueprintPosition { pub struct BlueprintPosition {
x: f64, pub x: f64,
y: f64, pub y: f64,
} }
impl BlueprintPosition { impl BlueprintPosition {
@ -279,19 +282,19 @@ impl BlueprintPosition {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintIcon { pub struct BlueprintIcon {
index: u32, index: u32,
signal: BlueprintSignalID, signal: BlueprintSignalID,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintTile { pub struct BlueprintTile {
name: String, name: String,
position: BlueprintPosition, position: BlueprintPosition,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BlueprintColor { pub struct BlueprintColor {
r: f32, r: f32,
g: f32, g: f32,

View file

@ -112,6 +112,7 @@ pub fn genetic_algorithm2<'a, R: Rng + ?Sized>(
problem: &'a Problem, problem: &'a Problem,
new_layouts: usize, new_layouts: usize,
mutation_timeout: usize, mutation_timeout: usize,
max_mutations: usize,
rng: &'_ mut R, rng: &'_ mut R,
) -> PathLayout<'a> { ) -> PathLayout<'a> {
let mut m = (0..new_layouts) let mut m = (0..new_layouts)
@ -119,19 +120,19 @@ pub fn genetic_algorithm2<'a, R: Rng + ?Sized>(
.min_by_key(|p| p.score()) .min_by_key(|p| p.score())
.unwrap(); .unwrap();
m.print_visualization(); // m.print_visualization();
let mut last_improvement = 0; let mut last_improvement = 0;
let mut count = 0; let mut count = 0;
while last_improvement < mutation_timeout { while last_improvement < mutation_timeout && count < max_mutations {
last_improvement += 1; last_improvement += 1;
count += 1; count += 1;
if let Some(p) = PathLayout::new(m.layout.mutate(rng)) { if let Some(p) = PathLayout::new(m.layout.mutate(rng)) {
if p.score() < m.score() { if p.score() < m.score() {
m = p; m = p;
println!("Step: {count}"); // println!("Step: {count}");
m.print_visualization(); // m.print_visualization();
last_improvement = 0; last_improvement = 0;
} }
} }
@ -207,15 +208,15 @@ impl<'a> PathLayout<'a> {
if !c.remove_all_conflicts(Some(std::time::Duration::from_secs(2))) { if !c.remove_all_conflicts(Some(std::time::Duration::from_secs(2))) {
if start.elapsed().as_secs_f32() > 0.5 { if start.elapsed().as_secs_f32() > 0.5 {
println!("Conflict avoidance: {:.2}", start.elapsed().as_secs_f32()); // println!("Conflict avoidance: {:.2}", start.elapsed().as_secs_f32());
c.print_visualization(); // c.print_visualization();
let file = std::fs::File::create(format!( let file = std::fs::File::create(format!(
"out/{}.json", "out/{}.json",
OUTFILEINDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed) OUTFILEINDEX.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
)) ))
.unwrap(); .unwrap();
serde_json::to_writer(file, &p).unwrap(); serde_json::to_writer(file, &p).unwrap();
println!("Saved slow solve."); // println!("Saved slow solve.");
} }
return None; return None;
} }
@ -419,7 +420,7 @@ impl Layout<'_> {
loop { loop {
let p = r.choose_weighted(rng, |i| i.1).unwrap(); let p = r.choose_weighted(rng, |i| i.1).unwrap();
if p.0(&mut s, rng) && rng.gen_bool(0.2) { if p.0(&mut s, rng) && rng.gen_bool(0.5) {
break; break;
} }
} }