Skip to content
This repository was archived by the owner on Jun 26, 2025. It is now read-only.

Commit d050aec

Browse files
Ashwin/slime chunk farm (#3)
* wipfeat: minecraft themed sliding window question * fix: whitespace * fix: add a reminder * feat: generate * feat: validate * chore: format * chore: reorganise functions * tidying up
1 parent 78d6d8c commit d050aec

File tree

4 files changed

+363
-0
lines changed

4 files changed

+363
-0
lines changed

competition/slime-chunk-farm/Cargo.lock

Lines changed: 158 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "slime-chunk-farm"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
rand = "0.9.1"
8+
rand_chacha = "0.9.0"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---toml
2+
[fuzz]
3+
exec = ["cargo", "run", "--release", "--", "generate"]
4+
env = {}
5+
6+
[judge]
7+
exec = ["cargo", "run", "--release", "--quiet", "--", "validate"]
8+
9+
[problem]
10+
points = 10
11+
difficulty = 1
12+
---
13+
14+
# 💡 Slime Chunk Farm
15+
16+
Steve would like to build a brand new Slime farm in his world. Since slimes only spawn in designated **Slime chunks** (16m*16m area where slimes can be found), Steve will need to find a suitable location for his farm.
17+
18+
He's only gathered enough resources to convert 50 slime chunks into farms, and he would like for the chunks to be as close together as possible.
19+
20+
It's up to you to help steve find the **smallest area of land** that contains atleast **50 slime chunks**!
21+
22+
## Input Format
23+
The input is a 100x100 grid of characters. Each character represents a chunk, which is a 16x16 area of land. A Slime chunk is denoted by an "**S**" while normal chunks are denoted by "**.**"
24+
25+
```
26+
...S.
27+
.S...
28+
.....
29+
.S..S
30+
.S.S.
31+
```
32+
33+
## Output Format
34+
Your output should be an integer representing the smallest area (in meters) in which 15 slime chunks could be found. (Remember that one chunk is (16*16) = 256 m^2)
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use std::{
2+
cmp,
3+
env::args,
4+
hash::{DefaultHasher, Hash, Hasher},
5+
io::stdin,
6+
iter::Sum,
7+
num::ParseIntError,
8+
ops::{AddAssign, Mul},
9+
process::exit,
10+
};
11+
12+
use rand::{Rng, SeedableRng};
13+
use rand_chacha::ChaChaRng;
14+
15+
const FARM_SIZE: u32 = 50;
16+
const GRID_SIZE: usize = 100;
17+
18+
#[derive(Debug)]
19+
struct PrefixSum2D {
20+
prefix: Vec<Vec<u32>>,
21+
}
22+
23+
impl PrefixSum2D {
24+
pub fn new(grid: &Vec<Vec<u32>>) -> Self {
25+
let mut prefix = vec![vec![0; grid[0].len()]; grid.len()];
26+
27+
prefix[0][0] = grid[0][0];
28+
for x in 1..grid[0].len() {
29+
prefix[0][x] = prefix[0][x - 1] + grid[0][x];
30+
}
31+
for y in 1..grid.len() {
32+
prefix[y][0] = prefix[y - 1][0] + grid[y][0];
33+
}
34+
35+
for y in 1..grid.len() {
36+
for x in 1..grid[0].len() {
37+
prefix[y][x] =
38+
prefix[y - 1][x] + prefix[y][x - 1] - prefix[y - 1][x - 1] + grid[y][x];
39+
}
40+
}
41+
42+
Self { prefix }
43+
}
44+
45+
// inclusive exclusive
46+
pub fn get_subrange_sum(&self, topleft: (usize, usize), bottomright: (usize, usize)) -> u32 {
47+
if bottomright.0 == 0 || bottomright.1 == 0 {
48+
return 0;
49+
}
50+
let sub_sum = self.prefix[bottomright.0 - 1][bottomright.1 - 1];
51+
if topleft.0 == 0 && topleft.1 == 0 {
52+
return sub_sum;
53+
} else if topleft.0 == 0 {
54+
return sub_sum - self.prefix[bottomright.0 - 1][topleft.1 - 1];
55+
} else if topleft.1 == 0 {
56+
return sub_sum - self.prefix[topleft.0 - 1][bottomright.1 - 1];
57+
}
58+
sub_sum + self.prefix[topleft.0 - 1][topleft.1 - 1]
59+
- self.prefix[topleft.0 - 1][bottomright.1 - 1]
60+
- self.prefix[bottomright.0 - 1][topleft.1 - 1]
61+
}
62+
}
63+
64+
65+
fn min_area_with_chunks(prefix_sum_world: &PrefixSum2D) -> u32 {
66+
let mut min_chunk_area = u32::MAX;
67+
for top in 0..prefix_sum_world.prefix.len() {
68+
for bottom in (top + 1)..prefix_sum_world.prefix.len() {
69+
for left in 0..prefix_sum_world.prefix[0].len() {
70+
for right in (left + 1)..prefix_sum_world.prefix[0].len() {
71+
if prefix_sum_world.get_subrange_sum((top, left), (bottom, right)) >= FARM_SIZE
72+
{
73+
// dbg!((top, left));
74+
// dbg!((bottom, right));
75+
min_chunk_area =
76+
cmp::min(min_chunk_area, ((bottom - top) * (right - left)) as u32);
77+
}
78+
}
79+
}
80+
}
81+
}
82+
83+
(16 * 16) * dbg!(min_chunk_area)
84+
}
85+
86+
fn generate(world: Vec<Vec<u32>>) -> String {
87+
world
88+
.iter()
89+
.map(|row| row
90+
.iter()
91+
.map(|chunk| {
92+
if *chunk == 0 {
93+
'.'
94+
} else {
95+
'S'
96+
}
97+
})
98+
.collect::<String>())
99+
.collect::<Vec<String>>()
100+
.join("\n")
101+
}
102+
103+
fn main() {
104+
let args: Vec<String> = args().collect();
105+
assert_eq!(args.len(), 3);
106+
107+
let mut default_hasher = DefaultHasher::new();
108+
args[2].hash(&mut default_hasher);
109+
let seed = default_hasher.finish();
110+
111+
let mut rng = ChaChaRng::seed_from_u64(seed);
112+
113+
let mut world = vec![vec![0; GRID_SIZE]; GRID_SIZE];
114+
// 10% chance of slime chunks
115+
// 0: normal
116+
// 1: slime chunk
117+
for _ in 0..((((GRID_SIZE * GRID_SIZE) as f32) * 0.1) as usize) {
118+
loop {
119+
let x = rng.random_range(0..GRID_SIZE);
120+
let y = rng.random_range(0..GRID_SIZE);
121+
if world[y][x] == 0 {
122+
world[y][x] = 1;
123+
break;
124+
}
125+
}
126+
}
127+
128+
let prefix_sum = PrefixSum2D::new(&world);
129+
130+
let solution = min_area_with_chunks(&prefix_sum);
131+
132+
match args[1].as_str() {
133+
// If `fuzz`-ing, just output the prompt.
134+
"generate" => {
135+
println!("{}", generate(world))
136+
}
137+
"validate" => {
138+
let mut buffer = String::new();
139+
let _ = stdin().read_line(&mut buffer);
140+
let input = buffer.trim().parse::<u32>();
141+
if let Ok(val) = input {
142+
if val == solution {
143+
exit(0);
144+
} else if val > solution {
145+
eprintln!("Incorrect, Your number is too high.");
146+
exit(1);
147+
} else {
148+
eprintln!("Incorrect, Your number is too low.");
149+
exit(1);
150+
}
151+
} else {
152+
eprintln!("Invalid input. Expected a positive number.");
153+
exit(1);
154+
}
155+
}
156+
// In this case, there is only one "correct" answer.
157+
// In debugging, this can be directly outputted.
158+
"solution" => {
159+
println!("{}", solution);
160+
}
161+
_ => panic!(),
162+
}
163+
}

0 commit comments

Comments
 (0)