September 2017 Update

This commit is contained in:
Ingy döt Net 2017-09-23 10:01:46 +02:00
parent bba7bfd280
commit ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions

View file

@ -0,0 +1,185 @@
Width := 10, Height := 10 ; set grid size
SleepTime := 0
gosub, Startup
Gui, +AlwaysOnTop
Gui, font, s12, consolas
Gui, add, edit, vEditGrid x10, % maze
Gui, add, button, xs gStartup Default, Generate maze
Gui, add, button, x+10 gSolve, Solve
Gui, show,, maze
GuiControl,, EditGrid, % maze ; show maze
return
;-----------------------------------------------------------------------
^Esc::
GuiEscape:
GuiClose:
ExitApp
return
;-----------------------------------------------------------------------
Startup:
oMaze := [] ; initialize
Solved := false
loop, % Height
{
row := A_Index
loop, % Width ; create oMaze[row,column] borders
col := A_Index, oMaze[row,col] := "LRTB" ; i.e. oMaze[2,5] := LRTB (add all borders)
}
Random, row, 1, % Height ; random row
Random, col, 1, % Width ; random col
grid := maze2text(oMaze) ; object to text
GuiControl,, EditGrid, % Grid ; show Grid
row := col := 1 ; reset to 1,1
oMaze := Generate_maze(row, col, oMaze) ; generate maze starting from random row/column
oMaze[1,1] .= "X" ; start from 1,1
maze := maze2text(oMaze) ; object to text
GuiControl,, EditGrid, % maze ; show maze
GuiControl,, EditRoute ; clear route
GuiControl, Enable, Solve
return
;-----------------------------------------------------------------------
Solve:
GuiControl, Disable, Generate maze
GuiControl, Disable, Solve
loop % oRoute.MaxIndex()
oRoute.pop()
oSolution := Solve(1, 1, oMaze) ; solve starting from 1,1
oMaze := oSolution.1
oRoute := oSolution.2
Update(oMaze, oRoute)
Solved := true
GuiControl, Enable, Generate maze
return
;-----------------------------------------------------------------------
Update(oMaze, oRoute){
global SleepTime
GuiControl,, EditGrid, % maze2text(oMaze)
Sleep, % SleepTime
}
;-----------------------------------------------------------------------
maze2text(oMaze){
width := oMaze.1.MaxIndex()
BLK := ""
for row, objRow in oMaze
{
for col, val in objRow ; add ceiling
{
ceiling := InStr(oMaze[row, col] , "x") && InStr(oMaze[row-1, col] , "x") ? "+ " BLK " " : "+ "
grid .= (InStr(val, "T") ? "+---" : ceiling) (col = Width ? "+`n" : "")
}
for col, val in objRow ; add left wall
{
wall := SubStr(val, 0) = "X" ? BLK : " "
grid .= (InStr(val, "L") ? "| " : " ") wall " " (col = Width ? "|`n" : "") ; add left wall if needed then outer right border
}
}
Loop % Width
Grid .= "+---" ; add bottom floor
Grid .= "+" ; add right bottom corner
return RegExReplace(grid , BLK " (?=" BLK ")" , BLK BLK BLK BLK) ; fill gaps
}
;-----------------------------------------------------------------------
Generate_maze(row, col, oMaze) {
neighbors := row+1 "," col "`n" row-1 "," col "`n" row "," col+1 "`n" row "," col-1
Sort, neighbors, random ; randomize neighbors list
Loop, parse, neighbors, `n ; for each neighbor
{
rowX := StrSplit(A_LoopField, ",").1 ; this neighbor row
colX := StrSplit(A_LoopField, ",").2 ; this neighbor column
if !instr(oMaze[rowX,colX], "LRTB") || !oMaze[rowX, colX] ; if visited (has a missing border) or out of bounds
continue ; skip
; remove borders
if (row > rowX) ; Cell is below this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "T") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "B")
else if (row < rowX) ; Cell is above this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "B") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "T")
else if (col > colX) ; Cell is right of this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "L") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "R")
else if (col < colX) ; Cell is left of this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "R") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "L")
Generate_maze(rowX, colX, oMaze) ; recurse for this neighbor
}
return, oMaze
}
;-----------------------------------------------------------------------
Solve(row, col, oMaze){
static oRoute := []
oNeighbor := [], targetrow := oMaze.MaxIndex(), targetCol := oMaze.1.MaxIndex()
;~ Update(oMaze, oRoute)
oRoute.push(row ":" col) ; push current cell address to oRoute
oMaze[row, col] .= "X" ; mark it visited "X"
if (row = targetrow) && (Col = targetCol) ; if solved
return true ; return ture
; create list of Neighbors
oNeighbor[row, col] := []
if !InStr(oMaze[row, col], "R") ; if no Right border
oNeighbor[row, col].push(row "," col+1) ; add neighbor
if !InStr(oMaze[row, col], "B") ; if no Bottom border
oNeighbor[row, col].push(row+1 "," col) ; add neighbor
if !InStr(oMaze[row, col], "T") ; if no Top border
oNeighbor[row, col].push(row-1 "," col) ; add neighbor
if !InStr(oMaze[row, col], "L") ; if no Left border
oNeighbor[row, col].push(row "," col-1) ; add neighbor
; recurese for each oNeighbor
for each, neighbor in oNeighbor[row, col] ; for each neighbor
{
Update(oMaze, oRoute)
startrow := StrSplit(neighbor, ",").1 ; this neighbor
startCol := StrSplit(neighbor, ",").2 ; becomes starting point
if !InStr(oMaze[startrow, startCol], "X") ; if it was not visited
if Solve(startrow, startCol, oMaze) ; recurse for current neighbor
return [oMaze, oRoute] ; return solution if solved
}
oRoute.pop() ; no solution found, back track
oMaze[row, Col] := StrReplace(oMaze[row, Col], "X") ; no solution found, back track
;~ Update(oMaze, oRoute)
}
;-----------------------------------------------------------------------
#IfWinActive, maze
Right::
Left::
Up::
Down::
if Solved
return
if (A_ThisHotkey="Right") && (!InStr(oMaze[row,col], "R"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , col++
if (A_ThisHotkey="Left") && (!InStr(oMaze[row,col], "L"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , col--
if (A_ThisHotkey="Up") && (!InStr(oMaze[row,col], "T"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , row--
if (A_ThisHotkey="Down") && (!InStr(oMaze[row,col], "B"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , row++
oMaze[row, col] .= "X"
GuiControl,, EditGrid, % maze2text(oMaze)
if (col = Width) && (row = Height)
{
Solved := true
oMaze[height, width] := StrReplace(oMaze[height, width], "X")
SleepTime := 0
gosub, solve
return
}
return
#IfWinActive

View file

@ -0,0 +1,106 @@
(ns small-projects.find-shortest-way
(:require [clojure.string :as str]))
;Misk functions
(defn cell-empty? [maze coords]
(= :empty (get-in maze coords)))
(defn wall? [maze coords]
(= :wall (get-in maze coords)))
(defn track? [maze coords]
(= :track (get-in maze coords)))
(defn get-neighbours [maze [y x cell]]
[[y (dec x)] [(inc y) x] [y (inc x)] [(dec y) x]])
(defn get-difference [coll1 filter-coll]
(filter #(not (contains? filter-coll %)) coll1))
(defn get-empties [maze cell]
(->> (get-neighbours maze cell)
(filter (partial cell-empty? maze))))
(defn possible-ways [maze cell filter-coll]
(-> (get-empties maze cell)
(get-difference filter-coll)))
(defn replace-cells [maze coords v]
(if (empty? coords)
maze
(recur (assoc-in maze (first coords) v) (rest coords) v)))
;Print and parse functions
(def cell-code->str
[" " " " " " " " "· " "╵ " "╴ " "┘ "
" " " " " " " " "╶─" "└─" "──" "┴─"
" " " " " " " " "╷ " "│ " "┐ " "┤ "
" " " " " " " " "┌─" "├─" "┬─" "┼─"
" " " " " " " " "■ " "╹ " "╸ " "┛ "
" " " " " " " " "╺━" "┗━" "━━" "┻━"
" " " " " " " " "╻ " "┃ " "┓ " "┫ "
" " " " " " " " "┏━" "┣━" "┳━" "╋━"
" "])
(defn get-cell-code [maze coords]
(let [mode (if (track? maze coords) 1 0)
check (if (zero? mode) wall? track?)]
(transduce
(comp
(map (partial check maze))
(keep-indexed (fn [idx test] (when test idx)))
(map (partial bit-shift-left 1)))
(completing bit-or)
(bit-shift-left mode 5)
(sort (conj (get-neighbours maze coords) coords)))))
(defn code->str [cell-code]
(nth cell-code->str cell-code))
(defn maze->str-symbols [maze]
(for [y (range (count maze))]
(for [x (range (count (nth maze y)))]
(code->str (get-cell-code maze [y x])))))
(defn maze->str [maze]
(->> (maze->str-symbols maze)
(map str/join)
(str/join "\n")))
(defn parse-pretty-maze [maze-str]
(->> (str/split-lines maze-str)
(map (partial take-nth 2))
(map (partial map #(if (= \space %) :empty :wall)))
(map vec)
(vec)))
;Core
(defn find-new-border [maze border old-border]
(apply conj (map (fn [cell]
(zipmap (possible-ways maze cell (conj border old-border))
(repeat cell)))
(keys border))))
(defn backtrack [visited route]
(let [cur-cell (get visited (first route))]
(if (= cur-cell :start)
route
(recur visited (conj route cur-cell)))))
(defn breadth-first-search [maze start-cell end-cell]
(loop [visited {start-cell :start}
border {start-cell :start}
old-border {start-cell :start}]
(if (contains? old-border end-cell)
(backtrack visited (list end-cell))
(recur
(conj visited border)
(find-new-border maze border old-border)
border))))
(def maze (parse-pretty-maze maze-str))
(def solved-maze
(replace-cells maze (breadth-first-search maze [1 1] [19 19]) :track))
(println (maze->str solved-maze))

View file

@ -13,6 +13,8 @@ bool solveMaze(char[][] maze, in V2 s, in V2 end) pure nothrow @safe @nogc {
foreach (immutable d; [V2(0, -cy), V2(+cx, 0), V2(0, +cy), V2(-cx, 0)])
if (maze[s.y + (d.y / 2)][s.x + (d.x / 2)] == ' ' &&
maze[s.y + d.y][s.x + d.x] == ' ') {
//Would this help?
// maze[s.y + (d.y / 2)][s.x + (d.x / 2)] = pathSymbol;
maze[s.y + d.y][s.x + d.x] = pathSymbol;
if (solveMaze(maze, V2(s.x + d.x, s.y + d.y), end))
return true;

View file

@ -1,10 +1,10 @@
#!/usr/bin/runhaskell
import Data.Maybe
import Data.Maybe (fromMaybe)
-- given two points, returns the average of them
average :: (Int, Int) -> (Int, Int) -> (Int, Int)
average (x, y) (x', y') = ((x + x') `div` 2, (y + y') `div` 2)
average (x, y) (x_, y_) = ((x + x_) `div` 2, (y + y_) `div` 2)
-- given a maze and a tuple of position and wall position, returns
-- true if the wall position is not blocked (first position is unused)
@ -22,41 +22,47 @@ substitute orig pos el =
-- given a maze and a position, draw a '*' at that position in the maze
draw :: [String] -> (Int, Int) -> [String]
draw maze (x,y) = substitute maze y $ substitute row x '*'
where row = maze !! y
draw maze (x, y) =
let row = maze !! y
in substitute maze y $ substitute row x '*'
-- given a maze, a previous position, and a list of tuples of potential
-- new positions and their wall positions, returns the solved maze, or
-- None if it cannot be solved
tryMoves :: [String] -> (Int, Int) -> [((Int, Int), (Int, Int))] -> Maybe [String]
tryMoves :: [String]
-> (Int, Int)
-> [((Int, Int), (Int, Int))]
-> Maybe [String]
tryMoves _ _ [] = Nothing
tryMoves maze prevPos ((newPos,wallPos):more) =
case solve' maze newPos prevPos
of Nothing -> tryMoves maze prevPos more
Just maze' -> Just $ foldl draw maze' [newPos, wallPos]
tryMoves maze prevPos ((newPos, wallPos):more) =
case solve_ maze newPos prevPos of
Nothing -> tryMoves maze prevPos more
Just maze_ -> Just $ foldl draw maze_ [newPos, wallPos]
-- given a maze, a new position, and a previous position, returns
-- the solved maze, or None if it cannot be solved
-- (assumes goal is upper-left corner of maze)
solve' :: [String] -> (Int, Int) -> (Int, Int) -> Maybe [String]
solve' maze (2, 1) _ = Just maze
solve' maze pos@(x, y) prevPos =
solve_ :: [String] -> (Int, Int) -> (Int, Int) -> Maybe [String]
solve_ maze (2, 1) _ = Just maze
solve_ maze pos@(x, y) prevPos =
let newPositions = [(x, y - 2), (x + 4, y), (x, y + 2), (x - 4, y)]
notPrev pos' = pos' /= prevPos
newPositions' = filter notPrev newPositions
wallPositions = map (average pos) newPositions'
zipped = zip newPositions' wallPositions
notPrev pos_ = pos_ /= prevPos
newPositions_ = filter notPrev newPositions
wallPositions = map (average pos) newPositions_
zipped = zip newPositions_ wallPositions
legalMoves = filter (notBlocked maze) zipped
in tryMoves maze pos legalMoves
-- given a maze, returns a solved maze, or None if it cannot be solved
-- (starts at lower right corner and goes to upper left corner)
solve :: [String] -> Maybe [String]
solve maze = solve' (draw maze start) start (-1, -1)
where startx = length (head maze) - 3
starty = length maze - 2
start = (startx, starty)
solve maze = solve_ (draw maze start) start (-1, -1)
where
startx = length (head maze) - 3
starty = length maze - 2
start = (startx, starty)
-- takes unsolved maze on standard input, prints solved maze on standard output
main = interact main'
where main' x = unlines $ fromMaybe ["can't solve"] $ solve $ lines x
main =
let main_ = unlines . fromMaybe ["can_t solve"] . solve . lines
in interact main_

View file

@ -0,0 +1,135 @@
var ctx, wid, hei, cols, rows, maze, stack = [], start = {x:-1, y:-1}, end = {x:-1, y:-1}, grid = 8;
function drawMaze() {
for( var i = 0; i < cols; i++ ) {
for( var j = 0; j < rows; j++ ) {
switch( maze[i][j] ) {
case 0: ctx.fillStyle = "black"; break;
case 1: ctx.fillStyle = "green"; break;
case 2: ctx.fillStyle = "red"; break;
case 3: ctx.fillStyle = "yellow"; break;
case 4: ctx.fillStyle = "#500000"; break;
}
ctx.fillRect( grid * i, grid * j, grid, grid );
}
}
}
function getFNeighbours( sx, sy, a ) {
var n = [];
if( sx - 1 > 0 && maze[sx - 1][sy] == a ) {
n.push( { x:sx - 1, y:sy } );
}
if( sx + 1 < cols - 1 && maze[sx + 1][sy] == a ) {
n.push( { x:sx + 1, y:sy } );
}
if( sy - 1 > 0 && maze[sx][sy - 1] == a ) {
n.push( { x:sx, y:sy - 1 } );
}
if( sy + 1 < rows - 1 && maze[sx][sy + 1] == a ) {
n.push( { x:sx, y:sy + 1 } );
}
return n;
}
function solveMaze() {
if( start.x == end.x && start.y == end.y ) {
for( var i = 0; i < cols; i++ ) {
for( var j = 0; j < rows; j++ ) {
switch( maze[i][j] ) {
case 2: maze[i][j] = 3; break;
case 4: maze[i][j] = 0; break;
}
}
}
drawMaze();
return;
}
var neighbours = getFNeighbours( start.x, start.y, 0 );
if( neighbours.length ) {
stack.push( start );
start = neighbours[0];
maze[start.x][start.y] = 2;
} else {
maze[start.x][start.y] = 4;
start = stack.pop();
}
drawMaze();
requestAnimationFrame( solveMaze );
}
function getCursorPos( event ) {
var rect = this.getBoundingClientRect();
var x = Math.floor( ( event.clientX - rect.left ) / grid ),
y = Math.floor( ( event.clientY - rect.top ) / grid );
if( maze[x][y] ) return;
if( start.x == -1 ) {
start = { x: x, y: y };
} else {
end = { x: x, y: y };
maze[start.x][start.y] = 2;
solveMaze();
}
}
function getNeighbours( sx, sy, a ) {
var n = [];
if( sx - 1 > 0 && maze[sx - 1][sy] == a && sx - 2 > 0 && maze[sx - 2][sy] == a ) {
n.push( { x:sx - 1, y:sy } ); n.push( { x:sx - 2, y:sy } );
}
if( sx + 1 < cols - 1 && maze[sx + 1][sy] == a && sx + 2 < cols - 1 && maze[sx + 2][sy] == a ) {
n.push( { x:sx + 1, y:sy } ); n.push( { x:sx + 2, y:sy } );
}
if( sy - 1 > 0 && maze[sx][sy - 1] == a && sy - 2 > 0 && maze[sx][sy - 2] == a ) {
n.push( { x:sx, y:sy - 1 } ); n.push( { x:sx, y:sy - 2 } );
}
if( sy + 1 < rows - 1 && maze[sx][sy + 1] == a && sy + 2 < rows - 1 && maze[sx][sy + 2] == a ) {
n.push( { x:sx, y:sy + 1 } ); n.push( { x:sx, y:sy + 2 } );
}
return n;
}
function createArray( c, r ) {
var m = new Array( c );
for( var i = 0; i < c; i++ ) {
m[i] = new Array( r );
for( var j = 0; j < r; j++ ) {
m[i][j] = 1;
}
}
return m;
}
function createMaze() {
var neighbours = getNeighbours( start.x, start.y, 1 ), l;
if( neighbours.length < 1 ) {
if( stack.length < 1 ) {
drawMaze(); stack = [];
start.x = start.y = -1;
document.getElementById( "canvas" ).addEventListener( "mousedown", getCursorPos, false );
return;
}
start = stack.pop();
} else {
var i = 2 * Math.floor( Math.random() * ( neighbours.length / 2 ) )
l = neighbours[i]; maze[l.x][l.y] = 0;
l = neighbours[i + 1]; maze[l.x][l.y] = 0;
start = l
stack.push( start )
}
drawMaze();
requestAnimationFrame( createMaze );
}
function createCanvas( w, h ) {
var canvas = document.createElement( "canvas" );
wid = w; hei = h;
canvas.width = wid; canvas.height = hei;
canvas.id = "canvas";
ctx = canvas.getContext( "2d" );
ctx.fillStyle = "black"; ctx.fillRect( 0, 0, wid, hei );
document.body.appendChild( canvas );
}
function init() {
cols = 73; rows = 53;
createCanvas( grid * cols, grid * rows );
maze = createArray( cols, rows );
start.x = Math.floor( Math.random() * ( cols / 2 ) );
start.y = Math.floor( Math.random() * ( rows / 2 ) );
if( !( start.x & 1 ) ) start.x++; if( !( start.y & 1 ) ) start.y++;
maze[start.x][start.y] = 0;
createMaze();
}

View file

@ -0,0 +1,58 @@
constant w = 11, h = 8
sequence wall = join(repeat("+",w+1),"---")&"\n",
cell = join(repeat("|",w+1)," ? ")&"\n",
grid = split(join(repeat(wall,h+1),cell),'\n')
procedure amaze(integer x, integer y)
grid[y][x] = ' ' -- mark cell visited
sequence p = shuffle({{x-4,y},{x,y+2},{x+4,y},{x,y-2}})
for i=1 to length(p) do
integer {nx,ny} = p[i]
if nx>1 and nx<w*4 and ny>1 and ny<=2*h and grid[ny][nx]='?' then
integer mx = (x+nx)/2
grid[(y+ny)/2][mx-1..mx+1] = ' ' -- knock down wall
amaze(nx,ny)
end if
end for
end procedure
integer dx,dy -- door location (in a wall!)
function solve_maze(integer x, y)
sequence p = {{x-4,y},{x,y+2},{x+4,y},{x,y-2}}
for d=1 to length(p) do
integer {nx,ny} = p[d]
integer {wx,wy} = {(x+nx)/2,(y+ny)/2}
if grid[wy][wx]=' ' then
grid[wy][wx] = "-:-:"[d] -- mark path
if {wx,wy}={dx,dy} then return true end if
if grid[ny][nx]=' ' then
grid[ny][nx] = 'o' -- mark cell
if solve_maze(nx,ny) then return true end if
grid[ny][nx] = ' ' -- unmark cell
end if
grid[wy][wx] = ' ' -- unmark path
end if
end for
return false
end function
function heads()
return rand(2)=1 -- toin coss 50:50 true(1)/false(0)
end function
integer {x,y} = {(rand(w)*4)-1,rand(h)*2}
amaze(x,y)
-- mark start pos
grid[y][x] = '*'
-- add a random door (heads=rhs/lhs, tails=top/btm)
if heads() then
{dy,dx} = {rand(h)*2,heads()*w*4+1}
grid[dy][dx] = ' '
else
{dy,dx} = {heads()*h*2+1,rand(w)*4-1}
grid[dy][dx-1..dx+1] = ' '
end if
{} = solve_maze(x,y)
puts(1,join(grid,'\n'))

View file

@ -0,0 +1,43 @@
ver,hor:=make_maze(); // see above link for this code
fcn munge(a,b){ String(a[0,2],b,a[3,*]) } // "+---" --> "+-*-"
fcn solveMaze(ver,hor, a,b, u,v, w,h){
if (a==u and b==v) return(True);
sh:=hor[b][a]; sv:=ver[b][a];
hor[b][a]=munge(sh,"*"); ver[b][a]=munge(sv,"*"); // drop breadcrumb
foreach dx,dy in (T( T(0,-1),T(1,0),T(0,1),T(-1,0) )){
x:=a+dx; y:=b+dy; hy:=hor[y]; vy:=ver[y];
if ( (0<=x<w) and (0<=y<h) and // (x,y) in bounds
(dx==0 or (dx== 1 and vy[x]==" ") or // horizontal
(dx==-1 and vy[a][0]==" " and vy[x][2]!="*")) and
(dy==0 or (dy== 1 and hy[x]=="+ ") or // vertical
(dy==-1 and hor[b][a][1]==" " and hy[x][2]!="*"))
)
{
sh:=hy[x];
if(solveMaze(ver,hor, x,y, u,v, w,h)){
hy[x]=sh; // remove splat on wall but not floor while backing out
return(True);
}
}
}
hor[b][a]=sh; ver[b][a]=sv; // pick up breadcrumb when backtracking
return(False);
}
w:=(hor[0].len() - 1); h:=(hor.len() - 1);
startx:=(0).random(w); starty:=(0).random(h);
endx :=(0).random(w); endy :=(0).random(h);
sh:=hor[starty][startx];
if (not solveMaze(ver,hor, startx,starty, endx,endy, w,h))
println("No solution path found.");
else{
hor[starty][startx]=sh;
ver[starty][startx]=munge(ver[starty][startx],"S");
ver[endy][endx] =munge(ver[endy][endx],"E");
foreach a,b in (hor.zip(ver)) { println(a.concat(),"\n",b.concat()) }
}