1// Obvious things left to do:2//3// Heuristic for choosing squares. We want the most amount of information4// per play, so that probably means going where we'd get a row and/or column to5// the lowest possible number, since we can then set a whole bunch of cells in6// one shot.7//8// Is it reasonable to try to be smart about whether setting water or ships?9//10// Should play more of the game to find more strategies to use.1112use std::fmt::Display;13use Cell::*;1415#[derive(Eq, PartialEq, PartialOrd, Ord, Clone, Copy, Debug)]16struct Point {17 row: i32,18 col: i32,19}2021#[derive(Copy, Clone)]22enum Direction {23 Up,24 Right,25 Down,26 Left,27}2829impl Direction {30 fn opposite(&self) -> Direction {31 match self {32 Direction::Up => Direction::Down,33 Direction::Right => Direction::Left,34 Direction::Down => Direction::Up,35 Direction::Left => Direction::Right,36 }37 }38}3940impl Point {41 fn new(col: i32, row: i32) -> Point {42 Point { row, col }43 }4445 fn travel(&self, d: Direction) -> Self {46 match d {47 Direction::Up => Point::new(self.col, self.row - 1),48 Direction::Right => Point::new(self.col + 1, self.row),49 Direction::Down => Point::new(self.col, self.row + 1),50 Direction::Left => Point::new(self.col - 1, self.row),51 }52 }5354 fn index(&self, size: i32) -> usize {55 (self.row * size + self.col) as usize56 }5758 fn neighbors_orthogonal(&self) -> [Point; 4] {59 [60 Point::new(self.col + 1, self.row),61 Point::new(self.col, self.row + 1),62 Point::new(self.col - 1, self.row),63 Point::new(self.col, self.row - 1),64 ]65 }6667 fn neighbors_diagonal(&self) -> [Point; 4] {68 [69 Point::new(self.col + 1, self.row + 1),70 Point::new(self.col - 1, self.row + 1),71 Point::new(self.col - 1, self.row - 1),72 Point::new(self.col + 1, self.row - 1),73 ]74 }7576 fn neighbors_all(&self) -> [Point; 8] {77 [78 Point::new(self.col + 1, self.row),79 Point::new(self.col + 1, self.row + 1),80 Point::new(self.col, self.row + 1),81 Point::new(self.col - 1, self.row + 1),82 Point::new(self.col - 1, self.row),83 Point::new(self.col - 1, self.row - 1),84 Point::new(self.col, self.row - 1),85 Point::new(self.col + 1, self.row - 1),86 ]87 }88}8990#[derive(Eq, PartialEq, Debug, PartialOrd, Ord, Clone, Copy)]91enum Cell {92 Empty,93 Water,94 Ship,95}9697enum InputCell {98 ShipSingle,99 ShipLeft,100 ShipRight,101 ShipTop,102 ShipBottom,103 ShipMiddle,104 Water,105}106107#[derive(Default, Eq, PartialEq, Debug, Clone)]108struct Game {109 cols: Vec<i32>,110 rows: Vec<i32>,111 board: Vec<Cell>,112 ships: Vec<i32>,113 size: i32,114}115116#[derive(Debug)]117enum SetResult {118 Ok,119 Bad,120 Solved,121}122123impl Display for Game {124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {125 write!(f, " ")?;126 for col in &self.cols {127 write!(f, "{} ", col)?;128 }129 writeln!(f)?;130131 for row in 0..self.size {132 write!(f, "{} ", self.rows[row as usize])?;133 for col in 0..self.size {134 let p = Point { row, col };135 match self.get(p) {136 Cell::Empty => write!(f, ". ")?,137 Cell::Ship => write!(f, "S ")?,138 Cell::Water => write!(f, "~ ")?,139 }140 }141 writeln!(f)?;142 }143144 Ok(())145 }146}147148static ts: &[Cell] = &[Cell::Water, Cell::Ship];149150impl Game {151 fn solve(&mut self) -> SetResult {152 //println!("{self}");153154 'outer: for row in 0..self.size {155 for col in 0..self.size {156 let p = Point { row, col };157 if self.get(p) != Cell::Empty {158 continue;159 }160161 for cell in ts {162 let mut new = self.clone();163 match new.set(p, *cell) {164 SetResult::Ok => (),165 SetResult::Bad => continue,166 SetResult::Solved => return SetResult::Solved,167 }168169 match new.solve() {170 SetResult::Ok => panic!("returned ok??"),171 SetResult::Bad => continue,172 SetResult::Solved => return SetResult::Solved,173 }174 }175176 break 'outer;177 }178 }179180 let ships = self.clone().num_ships();181182 if ships.len() != self.ships.len()183 || ships184 .iter()185 .zip(self.ships.iter())186 .any(|(one, two)| one != two)187 {188 SetResult::Bad189 } else {190 SetResult::Solved191 }192 }193194 fn flood_fill_ship(&mut self, p: Point) -> Option<i32> {195 // Flood fill. We will always hit from the top left, since196 // we iterate from the top left to bottom right.197198 debug_assert!(self.maybe_get(p).unwrap_or(Cell::Empty) == Cell::Ship);199200 let orig_p = p;201 let d = if self202 .maybe_get(p.travel(Direction::Right))203 .unwrap_or(Cell::Empty)204 == Cell::Ship205 {206 Direction::Right207 } else if self208 .maybe_get(p.travel(Direction::Down))209 .unwrap_or(Cell::Empty)210 == Cell::Ship211 {212 Direction::Down213 } else if p214 .neighbors_orthogonal()215 .into_iter()216 .any(|p| self.maybe_get(p).unwrap_or(Cell::Water) == Cell::Empty)217 {218 self.set_internal(p, Cell::Water);219 return None;220 } else {221 self.set_internal(p, Cell::Water);222 return Some(1);223 };224225 let mut p = p;226 let mut size = 0;227 while self.maybe_get(p).unwrap_or(Cell::Empty) == Cell::Ship {228 size += 1;229 self.set_internal(p, Cell::Water);230 p = p.travel(d);231 }232233 if self.maybe_get(p).unwrap_or(Cell::Water) != Cell::Water234 || self235 .maybe_get(orig_p.travel(d.opposite()))236 .unwrap_or(Cell::Water)237 != Cell::Water238 {239 None240 } else {241 debug_assert!(size != 0);242 Some(size)243 }244 }245246 fn num_ships(&mut self) -> Vec<i32> {247 let mut ships: Vec<i32> = Vec::with_capacity(15);248 for row in 0..self.size {249 for col in 0..self.size {250 let p = Point::new(col, row);251252 if self.get(p) == Cell::Ship {253 let f = self.flood_fill_ship(p);254 match f {255 Some(size) => {256 if size as usize >= ships.len() {257 ships.resize(size as usize + 1, 0);258 }259 ships[size as usize] += 1;260 }261 None => continue,262 }263 }264 }265 }266267 ships268 }269270 fn reduce_row<T>(&self, row: i32, init: T, f: fn(T, Cell) -> T) -> T {271 let mut p = Point { row, col: 0 };272 let mut acc = init;273 while p.col < self.size {274 acc = f(acc, self.get(p));275 p.col += 1;276 }277278 acc279 }280281 fn reduce_col<T>(&self, col: i32, init: T, f: fn(T, Cell) -> T) -> T {282 let mut p = Point { row: 0, col };283 let mut acc = init;284 while p.row < self.size {285 acc = f(acc, self.get(p));286 p.row += 1;287 }288289 acc290 }291292 fn set_internal(&mut self, p: Point, c: Cell) {293 debug_assert_ne!(c, Cell::Empty);294 self.board[p.index(self.size)] = c;295 }296297 fn set_maybe(&mut self, p: Point, c: Cell) -> SetResult {298 if p.row >= self.size || p.row < 0 || p.col >= self.size || p.col < 0 {299 SetResult::Ok300 } else {301 self.set(p, c)302 }303 }304305 fn set_input(&mut self, p: Point, c: InputCell) -> SetResult {306 match c {307 InputCell::ShipSingle => {308 match self.set(p, Cell::Ship) {309 SetResult::Ok => (),310 SetResult::Bad => return SetResult::Bad,311 SetResult::Solved => return SetResult::Solved,312 };313314 for p in p.neighbors_all() {315 match self.set_maybe(p, Cell::Water) {316 SetResult::Ok => (),317 SetResult::Bad => return SetResult::Bad,318 SetResult::Solved => return SetResult::Solved,319 };320 }321 }322323 InputCell::ShipLeft => {324 match self.set(p, Cell::Ship) {325 SetResult::Ok => (),326 SetResult::Bad => return SetResult::Bad,327 SetResult::Solved => return SetResult::Solved,328 };329330 match self.set(331 Point {332 row: p.row,333 col: p.col + 1,334 },335 Cell::Ship,336 ) {337 SetResult::Ok => (),338 SetResult::Bad => return SetResult::Bad,339 SetResult::Solved => return SetResult::Solved,340 };341342 match self.set_maybe(343 Point {344 row: p.row,345 col: p.col - 1,346 },347 Cell::Water,348 ) {349 SetResult::Ok => (),350 SetResult::Bad => return SetResult::Bad,351 SetResult::Solved => return SetResult::Solved,352 };353 }354 InputCell::ShipRight => {355 match self.set(p, Cell::Ship) {356 SetResult::Ok => (),357 SetResult::Bad => return SetResult::Bad,358 SetResult::Solved => return SetResult::Solved,359 };360361 match self.set(362 Point {363 row: p.row,364 col: p.col - 1,365 },366 Cell::Ship,367 ) {368 SetResult::Ok => (),369 SetResult::Bad => return SetResult::Bad,370 SetResult::Solved => return SetResult::Solved,371 };372373 match self.set_maybe(374 Point {375 row: p.row,376 col: p.col + 1,377 },378 Cell::Water,379 ) {380 SetResult::Ok => (),381 SetResult::Bad => return SetResult::Bad,382 SetResult::Solved => return SetResult::Solved,383 };384 }385 InputCell::ShipTop => {386 match self.set(p, Cell::Ship) {387 SetResult::Ok => (),388 SetResult::Bad => return SetResult::Bad,389 SetResult::Solved => return SetResult::Solved,390 };391392 match self.set(393 Point {394 row: p.row + 1,395 col: p.col,396 },397 Cell::Ship,398 ) {399 SetResult::Ok => (),400 SetResult::Bad => return SetResult::Bad,401 SetResult::Solved => return SetResult::Solved,402 };403404 match self.set_maybe(405 Point {406 row: p.row - 1,407 col: p.col,408 },409 Cell::Water,410 ) {411 SetResult::Ok => (),412 SetResult::Bad => return SetResult::Bad,413 SetResult::Solved => return SetResult::Solved,414 };415 }416 InputCell::ShipBottom => {417 match self.set(p, Cell::Ship) {418 SetResult::Ok => (),419 SetResult::Bad => return SetResult::Bad,420 SetResult::Solved => return SetResult::Solved,421 };422423 match self.set(424 Point {425 row: p.row - 1,426 col: p.col,427 },428 Cell::Ship,429 ) {430 SetResult::Ok => (),431 SetResult::Bad => return SetResult::Bad,432 SetResult::Solved => return SetResult::Solved,433 };434435 match self.set_maybe(436 Point {437 row: p.row + 1,438 col: p.col,439 },440 Cell::Water,441 ) {442 SetResult::Ok => (),443 SetResult::Bad => return SetResult::Bad,444 SetResult::Solved => return SetResult::Solved,445 };446 }447448 InputCell::ShipMiddle => match self.set(p, Cell::Ship) {449 SetResult::Ok => (),450 SetResult::Bad => return SetResult::Bad,451 SetResult::Solved => return SetResult::Solved,452 },453 InputCell::Water => match self.set(p, Cell::Water) {454 SetResult::Ok => (),455 SetResult::Bad => return SetResult::Bad,456 SetResult::Solved => return SetResult::Solved,457 },458 };459460 SetResult::Ok461 }462463 fn set(&mut self, p: Point, c: Cell) -> SetResult {464 if self.get(p) != Cell::Empty && self.get(p) != c {465 return SetResult::Bad;466 }467 if self.get(p) == c {468 return SetResult::Ok;469 }470 debug_assert_ne!(c, Cell::Empty);471472 let mut sets: Vec<(Point, Cell)> = Vec::new();473474 self.set_internal(p, c);475 if c == Cell::Ship {476 if self.cols[p.col as usize] == 0 || self.rows[p.row as usize] == 0 {477 return SetResult::Bad;478 }479480 self.cols[p.col as usize] -= 1;481 self.rows[p.row as usize] -= 1;482483 for p in p.neighbors_diagonal() {484 sets.push((p, Cell::Water));485 }486 }487488 let row_empty =489 self.reduce_row::<i32>(490 p.row,491 0,492 |acc, c| if c == Cell::Empty { acc + 1 } else { acc },493 );494495 // In this case, it's impossible to fill the row with ships496 if row_empty < self.rows[p.row as usize] {497 return SetResult::Bad;498 }499500 // Fill all empty cells in row if that's the only possibility.501 if row_empty == self.rows[p.row as usize] {502 let mut p = Point { row: p.row, col: 0 };503 while p.col < self.size {504 if self.get(p) == Cell::Empty {505 sets.push((p, Cell::Ship));506 }507508 p.col += 1509 }510 }511512 // Fill with water if there shouldn't be any more ships in this row.513 if self.rows[p.row as usize] == 0 {514 let mut p = Point { row: p.row, col: 0 };515 while p.col < self.size {516 if self.get(p) == Cell::Empty {517 sets.push((p, Cell::Water));518 }519520 p.col += 1;521 }522 }523524 let col_empty =525 self.reduce_col::<i32>(526 p.col,527 0,528 |acc, c| if c == Cell::Empty { acc + 1 } else { acc },529 );530 if col_empty < self.cols[p.col as usize] {531 return SetResult::Bad;532 }533 if col_empty == self.cols[p.col as usize] {534 let mut p = Point { row: 0, col: p.col };535 while p.row < self.size {536 if self.get(p) == Cell::Empty {537 sets.push((p, Cell::Ship));538 }539540 p.row += 1;541 }542 }543544 // Fill with water if there shouldn't be any more ships in this column.545 if self.cols[p.col as usize] == 0 {546 let mut p = Point { row: 0, col: p.col };547 while p.row < self.size {548 if self.get(p) == Cell::Empty {549 sets.push((p, Cell::Water));550 }551552 p.row += 1;553 }554 }555556 for set in sets {557 match self.set_maybe(set.0, set.1) {558 SetResult::Ok => (),559 SetResult::Bad => return SetResult::Bad,560 SetResult::Solved => return SetResult::Solved,561 };562 }563564 let ships = self.clone().num_ships();565 if ships.len() > self.ships.len()566 || ships567 .iter()568 .zip(self.ships.iter())569 .any(|(set, target)| *set > *target)570 {571 SetResult::Bad572 } else {573 SetResult::Ok574 }575 }576577 fn maybe_get(&self, p: Point) -> Option<Cell> {578 if p.row >= self.size || p.col >= self.size || p.row < 0 || p.col < 0 {579 None580 } else {581 Some(self.get(p))582 }583 }584585 fn get(&self, p: Point) -> Cell {586 debug_assert!(p.row < self.size);587 debug_assert!(p.col < self.size);588 debug_assert!(p.row >= 0);589 debug_assert!(p.col >= 0);590591 self.board[p.index(self.size)]592 }593}594595fn main() {596 // let mut game = Game {597 // cols: vec![1, 2, 2, 0, 2, 3],598 // rows: vec![3, 0, 3, 2, 0, 2],599 // board: vec![Empty; 8 * 8],600 // ships: vec![0, 3, 2, 1],601 // size: 6,602 // };603604 // println!(605 // "{:?}",606 // game.set_input(Point { row: 0, col: 1 }, InputCell::ShipSingle)607 // );608 // println!("{game}");609610 // let mut game = Game {611 // cols: vec![2, 0, 7, 0, 3, 0, 1, 4, 3, 6, 0, 4, 0, 2, 3],612 // rows: vec![3, 1, 3, 4, 3, 1, 1, 0, 5, 3, 4, 2, 2, 1, 2],613 // board: vec![Empty; 15 * 15],614 // ships: vec![0, 5, 4, 3, 2, 1],615 // size: 15,616 // };617618 // println!(619 // "{:?}",620 // game.set_input(Point { row: 0, col: 6 }, InputCell::ShipLeft)621 // );622 // println!("{game}");623 // println!(624 // "{:?}",625 // game.set_input(Point { row: 0, col: 8 }, InputCell::ShipRight)626 // );627 // println!("{game}");628 // println!(629 // "{:?}",630 // game.set_input(Point { row: 3, col: 4 }, InputCell::ShipTop)631 // );632 // println!("{game}");633 // println!(634 // "{:?}",635 // game.set_input(Point { row: 3, col: 8 }, InputCell::ShipLeft)636 // );637 // println!("{game}");638 // println!(639 // "{:?}",640 // game.set_input(Point { row: 5, col: 0 }, InputCell::ShipBottom)641 // );642 // println!("{game}");643 // println!(644 // "{:?}",645 // game.set_input(Point { row: 10, col: 13 }, InputCell::ShipSingle)646 // );647 // println!("{game}");648 // println!(649 // "{:?}",650 // game.set_input(Point { row: 11, col: 9 }, InputCell::ShipMiddle)651 // );652 // println!("{game}");653 // println!(654 // "{:?}",655 // game.set_input(Point { row: 11, col: 11 }, InputCell::ShipTop)656 // );657 // println!("{game}");658 // println!(659 // "{:?}",660 // game.set_input(Point { row: 13, col: 11 }, InputCell::ShipMiddle)661 // );662 // println!("{game}");663 // println!(664 // "{:?}",665 // game.set_input(Point { row: 14, col: 8 }, InputCell::ShipSingle)666 // );667 // println!("{game}");668669 let mut game = Game {670 cols: vec![3, 1, 1, 5, 2, 1, 1, 5, 1, 3, 2, 2, 4, 2, 2],671 rows: vec![3, 2, 2, 3, 1, 7, 0, 1, 0, 8, 0, 5, 1, 2, 0],672 board: vec![Empty; 15 * 15],673 ships: vec![0, 5, 4, 3, 2, 1],674 size: 15,675 };676677 println!("{:?}", game.set_input(Point::new(1, 0), InputCell::Water));678 println!(679 "{:?}",680 game.set_input(Point::new(7, 4), InputCell::ShipBottom)681 );682 println!(683 "{:?}",684 game.set_input(Point::new(10, 5), InputCell::ShipMiddle)685 );686 println!(687 "{:?}",688 game.set_input(Point::new(12, 7), InputCell::ShipSingle)689 );690 println!(691 "{:?}",692 game.set_input(Point::new(2, 9), InputCell::ShipLeft)693 );694 println!(695 "{:?}",696 game.set_input(Point::new(6, 11), InputCell::ShipLeft)697 );698 println!(699 "{:?}",700 game.set_input(Point::new(11, 11), InputCell::ShipLeft)701 );702 println!(703 "{:?}",704 game.set_input(Point::new(0, 12), InputCell::ShipMiddle)705 );706 println!(707 "{:?}",708 game.set_input(Point::new(10, 13), InputCell::ShipSingle)709 );710711 println!("{:?}", game.solve());712}