Compare commits
3 Commits
ddf734aed6
...
94cd28d403
Author | SHA1 | Date | |
---|---|---|---|
94cd28d403 | |||
475208def0 | |||
893ebef103 |
9
Makefile
Normal file
9
Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
CC=g++
|
||||
INCLUDE_DIR=include
|
||||
CFLAGS=-std=c++17 -fopenmp -Wall -O3 -march=native -I $(INCLUDE_DIR)
|
||||
|
||||
box-eater: optimizer/box-eater.cpp
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
box-breaker: optimizer/box-breaker.cpp
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
200
analyzer/index.html
Normal file
200
analyzer/index.html
Normal file
@ -0,0 +1,200 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Visual Pushbox 2024</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<div id="board">
|
||||
<div id="maze"></div>
|
||||
<div id="target" class="maze-sqr sqr-target"></div>
|
||||
<div id="player" class="maze-sqr maze-mv sqr-player"></div>
|
||||
<div id="box" class="maze-sqr maze-mv sqr-box"></div>
|
||||
</div>
|
||||
<div id="control-panel">
|
||||
<div class="ctrl-title">Analyzer</div>
|
||||
<table class="full-width info-table"><tbody>
|
||||
<tr><td>Analyze Status:</td><td id="analyze-status" class="text-right">Not Analyzed</td></tr>
|
||||
<tr><td>Total Steps:</td><td id="analyze-steps" class="text-right">N/A</td></tr>
|
||||
</tbody></table>
|
||||
<div class="btn-group full-width top-margin">
|
||||
<button id="analyze" class="btn float-left full-width">Analyze</button>
|
||||
</div>
|
||||
<div class="ctrl-title top-margin">Playback</div>
|
||||
<div id="progress-indicator">
|
||||
<div id="progress-value" class="float-left">415 / 1618</div>
|
||||
<div id="progress-left" class="float-right">-1203</div>
|
||||
</div>
|
||||
<div id="progress-bar" class="full-width top-margin">
|
||||
<div id="progress-bar-fg" class="full-height"></div>
|
||||
</div>
|
||||
<div class="ctrl-title top-margin">Import / Export</div>
|
||||
<textarea autocomplete="off" spellcheck="false" id="maze_charmap"></textarea>
|
||||
<div id="charmap_controls" class="btn-group full-width top-margin">
|
||||
<button id="charmap_import" class="btn float-left">Import</button>
|
||||
<button id="charmap_export" class="btn float-right">Export</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
<script type="module" src="index.js"></script>
|
||||
|
||||
<style>
|
||||
|
||||
#progress-bar {
|
||||
background-color: lightgrey;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
#progress-bar-fg {
|
||||
width: 30%;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-table, .info-table tr, .info-table td {
|
||||
border: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 40px;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0px;
|
||||
border: none;
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: grey;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none;
|
||||
outline: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.ctrl-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid black;
|
||||
font-size: x-large;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#control-panel {
|
||||
margin-left: 40px;
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
height: calc(80vh - 20px);
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.top-margin {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 30px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#maze_charmap {
|
||||
width: calc(100% - 2px);
|
||||
height: 298px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-size: 13.33px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#board {
|
||||
width: 80vh;
|
||||
height: 80vh;
|
||||
border: 1px solid black;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.maze-sqr {
|
||||
position: absolute;
|
||||
width: 4vh;
|
||||
height: 4vh;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#maze {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.maze-mv {
|
||||
z-index: 10;
|
||||
transition: top 0.2s, left 0.2s;
|
||||
}
|
||||
|
||||
.sqr-wall {
|
||||
background-color: grey;
|
||||
}
|
||||
|
||||
.sqr-space {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.sqr-target {
|
||||
background-color: lime;
|
||||
}
|
||||
|
||||
.sqr-player {
|
||||
background-color: lightblue;
|
||||
}
|
||||
|
||||
.sqr-box {
|
||||
background-color: orange;
|
||||
}
|
||||
</style>
|
||||
</html>
|
138
analyzer/index.js
Normal file
138
analyzer/index.js
Normal file
@ -0,0 +1,138 @@
|
||||
import { Vec2 } from './vec2.js';
|
||||
import { N, SQR_TYPE, Maze, AnalyzeContext, State } from './pushbox.js';
|
||||
|
||||
class BoardSquare {
|
||||
constructor(attach_element) {
|
||||
this.e = attach_element ?? document.createElement('div');
|
||||
this.e.classList.add('fixtl', 'maze-sqr');
|
||||
this.e.style.top = '0';
|
||||
this.e.style.left = '0';
|
||||
this.type = null;
|
||||
}
|
||||
|
||||
moveTo(x, y) {
|
||||
this.e.style.top = `${4 * x}vh`;
|
||||
this.e.style.left = `${4 * y}vh`;
|
||||
return this;
|
||||
}
|
||||
|
||||
moveToV(v) {
|
||||
return this.moveTo(v.x, v.y);
|
||||
}
|
||||
|
||||
setType(type) {
|
||||
if (this.type) {
|
||||
this.e.classList.remove(`sqr-${this.type}`);
|
||||
}
|
||||
this.type = type;
|
||||
this.e.classList.add(`sqr-${this.type}`);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BoardUI {
|
||||
constructor() {
|
||||
this.maze = document.getElementById('maze');
|
||||
this.target = new BoardSquare(document.getElementById('target'));
|
||||
this.box = new BoardSquare(document.getElementById('box'));
|
||||
this.player = new BoardSquare(document.getElementById('player'));
|
||||
this.squares = [];
|
||||
}
|
||||
|
||||
drawMaze(maze) {
|
||||
while (this.maze.hasChildNodes()) {
|
||||
this.maze.removeChild(this.maze.lastChild);
|
||||
}
|
||||
|
||||
this.squares = [];
|
||||
for (let i = 0; i < N; ++i) {
|
||||
for (let j = 0; j < N; ++j) {
|
||||
let sqr = new BoardSquare();
|
||||
sqr.moveTo(i, j);
|
||||
if (maze.get(i, j) == SQR_TYPE.WALL) {
|
||||
sqr.setType('wall');
|
||||
} else {
|
||||
sqr.setType('space');
|
||||
}
|
||||
this.maze.appendChild(sqr.e);
|
||||
}
|
||||
}
|
||||
this.target.moveToV(maze.target);
|
||||
}
|
||||
|
||||
updateState(state) {
|
||||
this.player.moveToV(state.player);
|
||||
this.box.moveToV(state.box);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const charmap_input = document.getElementById('maze_charmap');
|
||||
const analyze_button = document.getElementById('analyze');
|
||||
const analyze_status_element = document.getElementById('analyze-status');
|
||||
let board = new BoardUI();
|
||||
let maze = null, analyze_res = null;
|
||||
let current_worker = null;
|
||||
|
||||
function resetAnalyze() {
|
||||
document.getElementById('analyze-status').innerHTML = 'Not Analyzed';
|
||||
document.getElementById('analyze-steps').innerHTML = 'N/A';
|
||||
analyze_res = null;
|
||||
}
|
||||
|
||||
function importMaze(charmap_val) {
|
||||
let charmp = charmap_val.split('\n').map(x => x.trimEnd());
|
||||
maze = Maze.fromCharMap(charmp);
|
||||
board.drawMaze(maze);
|
||||
board.updateState(maze.init);
|
||||
analyze_button.disabled = false;
|
||||
resetAnalyze();
|
||||
}
|
||||
|
||||
if (localStorage.getItem('charmap-cache') != null) {
|
||||
charmap_input.value = localStorage.getItem('charmap-cache');
|
||||
importMaze(charmap_input.value);
|
||||
} else {
|
||||
analyze_button.disabled = true;
|
||||
}
|
||||
|
||||
document.getElementById('charmap_import').addEventListener('click', function() {
|
||||
localStorage.setItem('charmap-cache', charmap_input.value);
|
||||
importMaze(charmap_input.value);
|
||||
});
|
||||
|
||||
analyze_button.addEventListener('click', function() {
|
||||
resetAnalyze();
|
||||
if (current_worker != null) {
|
||||
current_worker.terminate();
|
||||
current_worker = null;
|
||||
analyze_button.innerHTML = 'Analyze';
|
||||
return;
|
||||
}
|
||||
|
||||
if (maze == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
current_worker = new Worker('solver.js?v=7', { type: 'module' });
|
||||
analyze_status_element.innerHTML = 'Dispatching';
|
||||
analyze_button.innerHTML = 'Stop';
|
||||
current_worker.onmessage = (msg_r) => {
|
||||
let msg = msg_r.data;
|
||||
if (msg.what === 'started') {
|
||||
analyze_status_element.innerHTML = 'Running';
|
||||
} else if (msg.what == 'done') {
|
||||
analyze_res = msg.value;
|
||||
console.log(analyze_res);
|
||||
analyze_status_element.innerHTML = 'Done';
|
||||
document.getElementById('analyze-steps').innerHTML = analyze_res.step.toString();
|
||||
current_worker.terminate();
|
||||
current_worker = null;
|
||||
analyze_button.innerHTML = 'Analyze';
|
||||
}
|
||||
};
|
||||
current_worker.postMessage(maze);
|
||||
});
|
||||
});
|
||||
|
209
analyzer/pushbox.js
Normal file
209
analyzer/pushbox.js
Normal file
@ -0,0 +1,209 @@
|
||||
import { Vec2 } from './vec2.js';
|
||||
import { Queue } from './queue.js';
|
||||
|
||||
export const N = 20;
|
||||
export const BSIZE = new Vec2(N, N);
|
||||
|
||||
export const SQR_TYPE = {
|
||||
WALL: 0,
|
||||
SPACE: 1,
|
||||
EXCEED: 2,
|
||||
};
|
||||
|
||||
export class State {
|
||||
constructor(player, box) {
|
||||
this.player = player ?? new Vec2(0, 0);
|
||||
this.box = box ?? new Vec2(1, 1);
|
||||
}
|
||||
|
||||
asHash() {
|
||||
return this.player.x + this.player.y * N
|
||||
+ this.box.x * N * N + this.box.y * N * N * N;
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return !this.player.equals(this.box) && this.player.isInside(Vec2.zero, BSIZE)
|
||||
&& this.box.isInside(Vec2.zero, BSIZE);
|
||||
}
|
||||
|
||||
isValidInMaze(maze) {
|
||||
return this.isValid() && maze.getV(this.player) && maze.getV(this.box);
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new State(this.player.clone(), this.box.clone());
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `[State: player ${this.player.toString()}, box ${this.box.toString()}]`;
|
||||
}
|
||||
|
||||
static fromRaw(o) {
|
||||
return new State(Vec2.fromRaw(o.player), Vec2.fromRaw(o.box));
|
||||
}
|
||||
}
|
||||
|
||||
export class Maze {
|
||||
constructor() {
|
||||
this.mp = new Array(N * N).fill(SQR_TYPE.SPACE);
|
||||
this.target = new Vec2(0, 0);
|
||||
this.init = new State();
|
||||
}
|
||||
|
||||
static fromRaw(o) {
|
||||
let res = new Maze();
|
||||
res.mp = o.mp;
|
||||
res.target = Vec2.fromRaw(o.target);
|
||||
res.init = State.fromRaw(o.init);
|
||||
return res;
|
||||
}
|
||||
|
||||
get(x, y) {
|
||||
return this.mp[x * N + y];
|
||||
}
|
||||
|
||||
getV(v) {
|
||||
return this.get(v.x, v.y);
|
||||
}
|
||||
|
||||
set(x, y, v) {
|
||||
this.mp[x * N + y] = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
setV(p, v) {
|
||||
return this.set(p.x, p.y, v);
|
||||
}
|
||||
|
||||
flip(x, y) {
|
||||
this.mp[x * N + y] ^= 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
flipV(p) {
|
||||
return this.flip(p.x, p.y);
|
||||
}
|
||||
|
||||
exportCharMap(s) {
|
||||
let state = s ?? this.init;
|
||||
let res = new Array(N);
|
||||
for (let i = 0; i < N; ++i) {
|
||||
let line = [];
|
||||
for (let j = 0; j < N; ++j) {
|
||||
let c = '.';
|
||||
if (this.get(i, j) == SQR_TYPE.WALL) {
|
||||
c = '#';
|
||||
}
|
||||
|
||||
if (this.target.match(i, j)) {
|
||||
c = 'O';
|
||||
} else if (state.player.match(i, j)) {
|
||||
c = 'P';
|
||||
} else if (state.target.match(i, j)) {
|
||||
c = '*';
|
||||
}
|
||||
line.push(c);
|
||||
}
|
||||
res[i] = line.join('');
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static fromCharMap(s) {
|
||||
let res = new Maze();
|
||||
for (let i = 0; i < N; ++i) {
|
||||
for (let j = 0; j < N; ++j) {
|
||||
if (s[i][j] == '#') {
|
||||
res.set(i, j, SQR_TYPE.WALL);
|
||||
} else {
|
||||
res.set(i, j, SQR_TYPE.SPACE);
|
||||
}
|
||||
|
||||
if (s[i][j] == 'O') {
|
||||
res.target = new Vec2(i, j);
|
||||
} else if (s[i][j] == 'P') {
|
||||
res.init.player = new Vec2(i, j);
|
||||
} else if (s[i][j] == '*') {
|
||||
res.init.box = new Vec2(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
export class AnalyzeContext {
|
||||
constructor(maze) {
|
||||
this.maze = maze;
|
||||
this.dis = new Array(N * N * N * N).fill(-1);
|
||||
this.source = new Array(N * N * N * N).fill(null);
|
||||
this.dis[maze.init.asHash()] = 0;
|
||||
|
||||
this.is_bfs_done = false;
|
||||
this.step = -1;
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
asResult() {
|
||||
return {
|
||||
is_bfs_done: this.is_bfs_done,
|
||||
dis: this.dis,
|
||||
source: this.source,
|
||||
step: this.step,
|
||||
path: this.path,
|
||||
};
|
||||
}
|
||||
|
||||
bfs() {
|
||||
const dt = [new Vec2(0, 1), new Vec2(0, -1), new Vec2(1, 0), new Vec2(-1, 0)];
|
||||
|
||||
if (this.is_bfs_done) {
|
||||
return this.step;
|
||||
}
|
||||
|
||||
let end_state = null;
|
||||
let q = new Queue();
|
||||
q.push(this.maze.init.clone());
|
||||
while (q.size()) {
|
||||
let x = q.front();
|
||||
q.pop();
|
||||
console.log(x.toString());
|
||||
|
||||
let dis = this.dis[x.asHash()];
|
||||
if (x.box.equals(this.maze.target) && this.step == -1) {
|
||||
this.step = dis;
|
||||
end_state = x;
|
||||
}
|
||||
|
||||
for (let d of dt) {
|
||||
let y = x.clone();
|
||||
y.player.addTo(d);
|
||||
if (y.player.equals(y.box)) {
|
||||
y.box.addTo(d);
|
||||
}
|
||||
|
||||
let yh = y.asHash();
|
||||
if (y.isValidInMaze(this.maze) && this.dis[yh] == -1) {
|
||||
console.log(y.toString());
|
||||
this.dis[yh] = dis + 1;
|
||||
this.source[yh] = x.clone();
|
||||
q.push(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.is_bfs_done = true;
|
||||
|
||||
if (this.step != -1) {
|
||||
this.path = [];
|
||||
let p = end_state;
|
||||
while (p != null) {
|
||||
this.path.push(p);
|
||||
p = this.source[p.asHash()];
|
||||
}
|
||||
this.path.reverse();
|
||||
}
|
||||
|
||||
return this.step;
|
||||
}
|
||||
};
|
||||
|
43
analyzer/queue.js
Normal file
43
analyzer/queue.js
Normal file
@ -0,0 +1,43 @@
|
||||
export class Queue {
|
||||
constructor() {
|
||||
this.val = [];
|
||||
this.ptr = 0;
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.val.length - this.ptr;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.size();
|
||||
}
|
||||
|
||||
empty() {
|
||||
return this.size() == 0;
|
||||
}
|
||||
|
||||
front() {
|
||||
return this.val[this.ptr];
|
||||
}
|
||||
|
||||
_rebuild() {
|
||||
if (this.ptr > 0) {
|
||||
this.val = this.val.slice(this.ptr);
|
||||
this.ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pop() {
|
||||
this.ptr += 1;
|
||||
if (this.ptr >= 16 && this.ptr >= this.val.length / 2) {
|
||||
this._rebuild();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
push(x) {
|
||||
this.val.push(x);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
9
analyzer/solver.js
Normal file
9
analyzer/solver.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { Maze, AnalyzeContext } from './pushbox.js';
|
||||
|
||||
onmessage = (msg) => {
|
||||
let ac = new AnalyzeContext(Maze.fromRaw(msg.data));
|
||||
postMessage({ what: 'started' });
|
||||
ac.bfs();
|
||||
postMessage({ what: 'done', value: ac.asResult() });
|
||||
};
|
||||
|
38
analyzer/vec2.js
Normal file
38
analyzer/vec2.js
Normal file
@ -0,0 +1,38 @@
|
||||
export class Vec2 {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
addTo(o) {
|
||||
this.x += o.x;
|
||||
this.y += o.y;
|
||||
}
|
||||
|
||||
isInside(lt, rb) {
|
||||
return this.x >= lt.x && this.x < rb.x && this.y >= lt.y && this.y < rb.y;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new Vec2(this.x, this.y);
|
||||
}
|
||||
|
||||
equals(o) {
|
||||
return this.x == o.x && this.y == o.y;
|
||||
}
|
||||
|
||||
match(x, y) {
|
||||
return this.x == x && this.y == y;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `(${this.x}, ${this.y})`;
|
||||
}
|
||||
|
||||
static fromRaw(o) {
|
||||
return new Vec2(o.x, o.y);
|
||||
}
|
||||
|
||||
static zero = new Vec2(0, 0);
|
||||
};
|
||||
|
@ -1,167 +1,19 @@
|
||||
#include <bits/stdc++.h>
|
||||
#include "pushbox.h"
|
||||
#include "box-solver.h"
|
||||
using namespace std;
|
||||
|
||||
const int N = 20;
|
||||
|
||||
mt19937 rng(random_device{}());
|
||||
uniform_real_distribution<float> d01(0, 1);
|
||||
uniform_int_distribution<int> rng10(0, 9);
|
||||
uniform_int_distribution<int> rngN(0, 19);
|
||||
|
||||
enum {
|
||||
POI_PERSON,
|
||||
POI_BOX,
|
||||
POI_TARGET,
|
||||
POI_EXCEED,
|
||||
};
|
||||
|
||||
#define compiler_assume(condition) \
|
||||
do { \
|
||||
if (!(condition)) \
|
||||
__builtin_unreachable(); \
|
||||
} while (false); \
|
||||
|
||||
struct Maze {
|
||||
bitset<N> M[N];
|
||||
int poi[3][2];
|
||||
|
||||
int at(int x, int y) const {
|
||||
return M[x][y];
|
||||
}
|
||||
|
||||
void set(int x, int y, int v) {
|
||||
M[x][y] = v;
|
||||
}
|
||||
|
||||
void flip(int x, int y) {
|
||||
M[x].flip(y);
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
for (int i = 0; i < POI_EXCEED; ++i) {
|
||||
for (int j : {0, 1}) {
|
||||
if (poi[i][j] < 0 || poi[i][j] >= N)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (at(poi[i][0], poi[i][1]) == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
inline Maze loadMaze(std::FILE *f) {
|
||||
char *s = new char[N + 5];
|
||||
Maze res;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
std::fscanf(f, "%s", s);
|
||||
for (int j = 0; j < N; ++j) {
|
||||
res.set(i, j, s[j] != '#');
|
||||
switch (s[j]) {
|
||||
case 'P':
|
||||
res.poi[POI_PERSON][0] = i;
|
||||
res.poi[POI_PERSON][1] = j;
|
||||
break;
|
||||
case '*':
|
||||
res.poi[POI_BOX][0] = i;
|
||||
res.poi[POI_BOX][1] = j;
|
||||
break;
|
||||
case 'O':
|
||||
res.poi[POI_TARGET][0] = i;
|
||||
res.poi[POI_TARGET][1] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] s;
|
||||
return res;
|
||||
}
|
||||
|
||||
inline void writeMaze(const Maze &m, std::FILE *f) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (int j = 0; j < N; ++j) {
|
||||
if (i == m.poi[POI_PERSON][0] && j == m.poi[POI_PERSON][1])
|
||||
std::fputc('P', f);
|
||||
else if (i == m.poi[POI_BOX][0] && j == m.poi[POI_BOX][1])
|
||||
std::fputc('*', f);
|
||||
else if (i == m.poi[POI_TARGET][0] && j == m.poi[POI_TARGET][1])
|
||||
std::fputc('O', f);
|
||||
else if (m.at(i, j) == 0)
|
||||
std::fputc('#', f);
|
||||
else
|
||||
std::fputc('.', f);
|
||||
}
|
||||
std::fputc('\n', f);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Solve {
|
||||
bitset<N> mp[N];
|
||||
int dis[N][N][N][N];
|
||||
queue<tuple<int, int, int, int>> qu;
|
||||
|
||||
inline int bfs(int px, int py, int bx, int by, int tx, int ty) {
|
||||
memset(dis, 127, sizeof(dis));
|
||||
while (!qu.empty())
|
||||
qu.pop();
|
||||
|
||||
auto expand = [&](int npx, int npy, int nbx, int nby, int d) {
|
||||
if (npx < 0 || npx >= N)
|
||||
return;
|
||||
if (npy < 0 || npy >= N)
|
||||
return;
|
||||
if (nbx < 0 || nbx >= N)
|
||||
return;
|
||||
if (nby < 0 || nby >= N)
|
||||
return;
|
||||
if (mp[npx][npy] == 0)
|
||||
return;
|
||||
if (mp[nbx][nby] == 0)
|
||||
return;
|
||||
if (npx == nbx && npy == nby)
|
||||
return;
|
||||
|
||||
if (dis[npx][npy][nbx][nby] <= d)
|
||||
return;
|
||||
dis[npx][npy][nbx][nby] = d;
|
||||
qu.emplace(npx, npy, nbx, nby);
|
||||
};
|
||||
|
||||
expand(px, py, bx, by, 0);
|
||||
|
||||
while (!qu.empty()) {
|
||||
auto [npx, npy, nbx, nby] = qu.front();
|
||||
int d = dis[npx][npy][nbx][nby];
|
||||
qu.pop();
|
||||
|
||||
if (nbx == tx && nby == ty)
|
||||
return d;
|
||||
|
||||
expand(npx + 1, npy, nbx, nby, d + 1);
|
||||
expand(npx - 1, npy, nbx, nby, d + 1);
|
||||
expand(npx, npy + 1, nbx, nby, d + 1);
|
||||
expand(npx, npy - 1, nbx, nby, d + 1);
|
||||
if (npx + 1 == nbx && npy == nby)
|
||||
expand(npx + 1, npy, nbx + 1, nby, d + 1);
|
||||
if (npx - 1 == nbx && npy == nby)
|
||||
expand(npx - 1, npy, nbx - 1, nby, d + 1);
|
||||
if (npx == nbx && npy + 1 == nby)
|
||||
expand(npx, npy + 1, nbx, nby + 1, d + 1);
|
||||
if (npx == nbx && npy - 1 == nby)
|
||||
expand(npx, npy - 1, nbx, nby - 1, d + 1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline int solve(Maze q) {
|
||||
for (int i = 0; i < 20; ++i)
|
||||
mp[i] = q.M[i];
|
||||
return bfs(q.poi[POI_PERSON][0], q.poi[POI_PERSON][1]
|
||||
, q.poi[POI_BOX][0], q.poi[POI_BOX][1], q.poi[POI_TARGET][0], q.poi[POI_TARGET][1]);
|
||||
}
|
||||
}; // namespace Solve
|
||||
|
||||
const float initial_temperature = 10;
|
||||
const float initial_temperature_pv = 60;
|
||||
const float termperature_delta = .97;
|
||||
|
Loading…
x
Reference in New Issue
Block a user