Skip to content

Commit 13aef42

Browse files
committed
Add score calculation
1 parent 3f8541b commit 13aef42

File tree

3 files changed

+355
-4
lines changed

3 files changed

+355
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
**/*.rs.bk
3+
solutions/test

src/bin/score.rs

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
use clap::{App, Arg};
2+
use glob::glob;
3+
use std::cmp;
4+
use std::collections::{HashMap, VecDeque};
5+
use std::fs::File;
6+
use std::io::Read;
7+
use std::iter::Peekable;
8+
use std::str::Chars;
9+
10+
fn find_files(input_root: &str) -> Vec<String> {
11+
glob(&format!("{}/prob-*.desc", input_root))
12+
.expect("glob pattern")
13+
.map(|p| {
14+
p.unwrap()
15+
.file_name()
16+
.unwrap()
17+
.to_str()
18+
.unwrap()
19+
.to_string()
20+
})
21+
.collect::<Vec<String>>()
22+
}
23+
24+
fn output_file_name(file_name: &str) -> String {
25+
format!("prob-{}.sol", &file_name[5..8])
26+
}
27+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
28+
struct Point {
29+
x: usize,
30+
y: usize,
31+
}
32+
impl Point {
33+
fn new(x: usize, y: usize) -> Point {
34+
Point { x, y }
35+
}
36+
37+
fn move_with(&self, command: Command) -> Option<Point> {
38+
let (x, y) = (self.x, self.y);
39+
match command {
40+
Command::MoveUp => Some(Point::new(x, y + 1)),
41+
Command::MoveDown => match self.y {
42+
0 => None,
43+
_ => Some(Point::new(x, y - 1)),
44+
},
45+
Command::MoveRight => Some(Point::new(x + 1, y)),
46+
Command::MoveLeft => match self.x {
47+
0 => None,
48+
_ => Some(Point::new(x - 1, y)),
49+
},
50+
_ => Some(*self),
51+
}
52+
}
53+
}
54+
55+
#[derive(Debug, Clone)]
56+
enum BoosterType {
57+
NewHand,
58+
FastMove,
59+
Drill,
60+
Unknown,
61+
}
62+
63+
type Booster = (BoosterType, Point);
64+
65+
#[derive(Debug, Clone)]
66+
struct Map(Vec<Point>);
67+
68+
enum Direction {
69+
Verticle,
70+
Horizontal,
71+
}
72+
73+
impl Map {
74+
fn iter_lines(&self) -> Vec<(Direction, Point, Point)> {
75+
let mut iter = self.0.iter().cloned().cycle().peekable();
76+
let mut res = Vec::new();
77+
for _ in 0..self.0.len() {
78+
let cur = iter.next().unwrap();
79+
let next = *iter.peek().unwrap();
80+
if cur.x == next.x {
81+
res.push((Direction::Verticle, cur, next));
82+
} else if cur.y == next.y {
83+
res.push((Direction::Horizontal, cur, next));
84+
} else {
85+
unreachable!();
86+
}
87+
}
88+
res
89+
}
90+
91+
fn enumerate_points(&self) -> Vec<Point> {
92+
let g_min_x = self.0.iter().map(|p| p.x).min().unwrap();
93+
let g_max_x = self.0.iter().map(|p| p.x).max().unwrap();
94+
let mut cross_y_map = HashMap::new();
95+
for (dir, p, q) in self.iter_lines() {
96+
if let Horizontal = dir {
97+
let min_x = cmp::min(p.x, q.x);
98+
let max_x = cmp::max(p.x, q.x);
99+
for x in min_x..max_x {
100+
cross_y_map.entry(x).or_insert_with(|| Vec::new()).push(p.y);
101+
}
102+
}
103+
}
104+
let mut res = Vec::new();
105+
for x in g_min_x..g_max_x {
106+
let v = cross_y_map.get_mut(&x).unwrap();
107+
assert!(v.len() % 2 == 0);
108+
v.sort();
109+
let mut iter = v.iter();
110+
while let Some(lb) = iter.next() {
111+
let ub = iter.next().unwrap();
112+
for y in *lb..*ub {
113+
res.push(Point::new(x, y));
114+
}
115+
}
116+
}
117+
res
118+
}
119+
}
120+
121+
#[derive(Debug, Clone)]
122+
struct Input {
123+
map: Map,
124+
initial: Point,
125+
obstacles: Vec<Map>,
126+
boosters: Vec<Booster>,
127+
}
128+
129+
#[derive(Debug, Clone, Copy)]
130+
enum Command {
131+
MoveUp,
132+
MoveDown,
133+
MoveLeft,
134+
MoveRight,
135+
Noop,
136+
TurnRight,
137+
TurnLeft,
138+
}
139+
140+
fn skip(iter: &mut Peekable<Chars>, expected: char) {
141+
let c = iter.next().unwrap();
142+
assert!(c == expected);
143+
}
144+
145+
fn skip_or_empty(iter: &mut Peekable<Chars>, expected: char) {
146+
if let Some(c) = iter.next() {
147+
assert!(c == expected);
148+
}
149+
}
150+
151+
fn read_point(iter: &mut Peekable<Chars>) -> Point {
152+
skip(iter, '(');
153+
let mut x = 0;
154+
loop {
155+
let c = iter.next().unwrap();
156+
if c.is_digit(10) {
157+
x = x * 10 + (c as u8 - '0' as u8);
158+
} else {
159+
assert!(c == ',');
160+
break;
161+
}
162+
}
163+
let mut y = 0;
164+
loop {
165+
let c = iter.next().unwrap();
166+
if c.is_digit(10) {
167+
y = y * 10 + (c as u8 - '0' as u8);
168+
} else {
169+
assert!(c == ')');
170+
break;
171+
}
172+
}
173+
Point::new(x as usize, y as usize)
174+
}
175+
176+
fn read_map_internal(mut iter: &mut Peekable<Chars>) -> (Map, char) {
177+
let mut points = Vec::new();
178+
points.push(read_point(&mut iter));
179+
loop {
180+
let c = iter.next().unwrap();
181+
if c != ',' {
182+
return (Map(points), c);
183+
}
184+
points.push(read_point(&mut iter));
185+
}
186+
}
187+
188+
fn read_map(mut iter: &mut Peekable<Chars>) -> Map {
189+
let (m, c) = read_map_internal(&mut iter);
190+
assert!(c == '#');
191+
m
192+
}
193+
194+
fn read_initial(mut iter: &mut Peekable<Chars>) -> Point {
195+
let p = read_point(&mut iter);
196+
skip(iter, '#');
197+
p
198+
}
199+
200+
fn read_obstacles(mut iter: &mut Peekable<Chars>) -> Vec<Map> {
201+
let mut res = Vec::new();
202+
if *iter.peek().unwrap() == '#' {
203+
iter.next();
204+
return res;
205+
}
206+
207+
loop {
208+
let (m, c) = read_map_internal(&mut iter);
209+
res.push(m);
210+
if c == '#' {
211+
break;
212+
}
213+
assert!(c == ';');
214+
}
215+
res
216+
}
217+
218+
fn read_boosters(mut iter: &mut Peekable<Chars>) -> Vec<Booster> {
219+
let mut res = Vec::new();
220+
while let Some(c) = iter.next() {
221+
let booster_type = match c {
222+
'B' => BoosterType::NewHand,
223+
'F' => BoosterType::FastMove,
224+
'L' => BoosterType::Drill,
225+
'X' => BoosterType::Unknown,
226+
_ => panic!("unknown type {}", c),
227+
};
228+
let point = read_point(&mut iter);
229+
res.push((booster_type, point));
230+
skip_or_empty(&mut iter, ';');
231+
}
232+
res
233+
}
234+
235+
fn read_input(s: &str) -> Input {
236+
let mut iter = s.chars().peekable();
237+
let map = read_map(&mut iter);
238+
let initial = read_initial(&mut iter);
239+
let obstacles = read_obstacles(&mut iter);
240+
let boosters = read_boosters(&mut iter);
241+
Input {
242+
map,
243+
initial,
244+
obstacles,
245+
boosters,
246+
}
247+
}
248+
249+
struct ScoreInfo {
250+
width: usize,
251+
height: usize,
252+
best_estimated: usize,
253+
team_time: usize,
254+
}
255+
256+
impl ScoreInfo {
257+
fn log_wh(&self) -> f64 {
258+
let wh = self.width as f64 * self.height as f64;
259+
wh.log2()
260+
}
261+
262+
fn ratio(&self) -> f64 {
263+
self.best_estimated as f64 / self.team_time as f64
264+
}
265+
266+
fn debug(&self) -> String {
267+
format!(
268+
"1000.0 * {:5.2} * {:4.2} = {:8.2}",
269+
self.log_wh(),
270+
self.ratio(),
271+
self.score()
272+
)
273+
}
274+
275+
fn score(&self) -> f64 {
276+
1000.0 * self.log_wh() * self.ratio()
277+
}
278+
}
279+
280+
fn score_small(input: Input, output_len: usize) -> ScoreInfo {
281+
let map_points = input.map.enumerate_points();
282+
283+
let width = map_points.iter().map(|p| p.x).max().unwrap() + 1;
284+
let height = map_points.iter().map(|p| p.y).max().unwrap() + 1;
285+
let mut remaining = 0;
286+
let mut passed = vec![vec![true; width]; height];
287+
let mut valid = vec![vec![false; width]; height];
288+
289+
for p in &map_points {
290+
passed[p.y][p.x] = false;
291+
valid[p.y][p.x] = true;
292+
remaining += 1;
293+
}
294+
295+
for o in &input.obstacles {
296+
for p in o.enumerate_points().iter() {
297+
if p.y < height && p.x < width && valid[p.y][p.x] {
298+
valid[p.y][p.x] = false;
299+
passed[p.y][p.x] = true;
300+
remaining -= 1;
301+
}
302+
}
303+
}
304+
305+
ScoreInfo {
306+
width,
307+
height,
308+
best_estimated: remaining,
309+
team_time: output_len,
310+
}
311+
}
312+
313+
fn main() {
314+
let matches = App::new("Score checker")
315+
.version("0.1.0")
316+
.arg(
317+
Arg::with_name("input")
318+
.long("input")
319+
.takes_value(true)
320+
.help("input root directory"),
321+
)
322+
.arg(
323+
Arg::with_name("output")
324+
.long("output")
325+
.takes_value(true)
326+
.help("output directory"),
327+
)
328+
.get_matches();
329+
let input_root = matches.value_of("input").expect("no input specified");
330+
let output_root = matches.value_of("output").expect("no output specified");
331+
let files = find_files(&input_root);
332+
333+
let mut sum_scores = 0.0;
334+
for f in files.iter() {
335+
let input_path = format!("{}/{}", input_root, f);
336+
let mut input_file = File::open(&input_path).unwrap();
337+
let output_path = format!("{}/{}", output_root, output_file_name(&f));
338+
let mut output_file = File::open(&output_path).unwrap();
339+
let mut input_str = String::new();
340+
input_file.read_to_string(&mut input_str);
341+
let mut output_str = String::new();
342+
output_file.read_to_string(&mut output_str);
343+
let output_len = output_str.trim_end().len();
344+
let score_info = score_small(read_input(&input_str), output_len);
345+
eprintln!("{}: {}", f, score_info.debug());
346+
sum_scores += score_info.score();
347+
}
348+
println!("output: {}", output_root);
349+
println!("total_score: {}", sum_scores);
350+
}

src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl Point {
7272
}
7373
}
7474

75-
#[derive(Debug)]
75+
#[derive(Debug, Clone)]
7676
struct Map(Vec<Point>);
7777

7878
enum Direction {
@@ -128,7 +128,7 @@ impl Map {
128128
}
129129
}
130130

131-
#[derive(Debug)]
131+
#[derive(Debug, Clone)]
132132
enum BoosterType {
133133
NewHand,
134134
FastMove,
@@ -138,7 +138,7 @@ enum BoosterType {
138138

139139
type Booster = (BoosterType, Point);
140140

141-
#[derive(Debug)]
141+
#[derive(Debug, Clone)]
142142
struct Input {
143143
map: Map,
144144
initial: Point,
@@ -384,7 +384,7 @@ fn main() {
384384
.long("number")
385385
.short("n")
386386
.takes_value(true)
387-
.help("output directory"),
387+
.help("number of test cases to solve"),
388388
)
389389
.get_matches();
390390

0 commit comments

Comments
 (0)