HappyCube Puzzle Solver

9

Tantangan ini terinspirasi oleh puzzle yang saya mainkan, yang terdiri dari potongan busa seperti ini:

potongan puzzle

yang harus dirakit menjadi kubus 3D, seperti ini:

kubus dipecahkan

Potongan-potongan puzzle dapat dilihat sebagai kotak 5 * 5 kotak, yang kotak 3 * 3 tengahnya selalu solid, sedangkan 16 kotak di tepi bisa padat atau kosong.

Sepotong akan dijelaskan menggunakan string 16 karakter ( 0s dan 1s), mewakili konfigurasi ujungnya ( 0= kosong, 1= padat), dalam urutan searah jarum jam, mulai dari sudut kiri atas.

Misalnya, string:

0101001000101101

mewakili bagian ini:

 # #
####
 ####
####
# #

Untuk menyatukan potongan-potongan itu untuk membentuk kubus, setiap bagian dapat diputar ke segala arah. Sebagai contoh, ini adalah rotasi sah dari bagian yang ditunjukkan di atas:

 # #    # #     #    ## # 
 ####  ####    ####   ####
####    ####  ####   #### 
 ####  ####    ####   ####
  # #  # #    ## #     #  

# #      # #   # ##    #  
####    ####  ####   #### 
 ####  ####    ####   ####
####    ####  ####   #### 
 # #    # #     #     # ##

Tantangan

Tulis program atau fungsi yang mengambil 6 potongan puzzle dan cetak atau kembalikan representasi 2D dari kubus yang diselesaikan.

Memasukkan

Input akan berupa string 6 baris, di mana setiap baris terdiri dari 16 0atau 1karakter, mewakili tepi sepotong (dalam format yang dijelaskan di atas).

Dapat diasumsikan bahwa ada solusi untuk input.

Baris baru tambahan adalah opsional.

Keluaran

Hasilnya akan menjadi representasi ASCII dari kubus yang diselesaikan, dibuka dalam 2D, seperti ini (diagram menggunakan notasi Rubik's Cube untuk nama samping):

    +---+
    |BA |
    |CK |
    |   |
+---+---+---+---+
|LE |DO |RI |UP |
|FT |WN |GHT|   |
|   |   |   |   |
+---+---+---+---+
    |FR |
    |ONT|
    |   |
    +---+

Untuk menghindari kemungkinan menghadirkan solusi dalam berbagai cara, karya yang ditempatkan BAWAH akan selalu menjadi karya pertama yang ada dalam input, dalam rotasi yang sama seperti yang ditentukan di sana.

Setiap bagian akan direpresentasikan secara grafis sebagai matriks 5 * 5, menggunakan spasi untuk menunjukkan kotak kosong. Untuk kotak padat, Anda dapat menggunakan karakter non-spasi apa pun yang Anda inginkan, asalkan:

  • setiap potongan puzzle akan memiliki kotak yang solid diwakili menggunakan karakter yang sama
  • setiap dua buah yang berdekatan menggunakan karakter yang berbeda

Padding ruang di sebelah kanan dan baris baru tambahan adalah opsional.

Uji Kasus

1.

Memasukkan:

0010010101010101
0010001011011010
0101001001010010
0010110100101101
0010110110101101
0010001011010101

Keluaran:

     @ @         
     @@@         
    @@@@@        
     @@@         
** **@#@** *# #  
 ***#####***#####
*****###*****### 
 ***#####***#####
  * @#@#** ** # #
    @@@@         
     @@@@        
    @@@@         
     @ @         

2.

Memasukkan:

0001110110101101
1010010111011101
0101010101010010
1010001000100011
1010001001010001
0110010100100010

Keluaran:

      @          
     @@@@        
    @@@@         
     @@@@        
** **@@## * *# # 
****#####****### 
 ****###*****####
****#####***#### 
** *#@#@# * # #  
     @@@@        
    @@@@         
     @@@@        
     @ @         

3.

Memasukkan:

0101001011011010
0010001000100010
0101001011010010
0101010101011010
0101101001011101
1010001001011101

Keluaran:

     @ @@        
    @@@@@        
     @@@         
    @@@@@        
* * @#@#* *   #  
*****###*****### 
 ***#####***#####
*****###*****### 
  * ##@##* *  #  
    @@@@         
     @@@@        
    @@@@         
    @@ @@        

Ini codegolf, jadi program terpendek dalam byte menang.

Cristian Lupascu
sumber
Ya, setidaknya pada tampilan saya, "kembali", "turun" dan "depan" terlihat seperti warna / karakter yang sama.
Reto Koradi
"Untuk menghindari kemungkinan menghadirkan solusi dengan berbagai cara, bagian yang ditempatkan TURUN akan selalu menjadi bagian pertama yang ada dalam input, dalam rotasi yang sama seperti yang ditentukan di sana." Bahkan jika Anda memegang potongan puzzle pertama secara konstan, saya tidak berpikir bahwa lima potongan sisanya dijamin memiliki lokasi yang unik.
Rainbolt
@ Rainbolt Untuk input yang saya gunakan, itu benar - hanya ada satu cara mengatur output. Namun secara umum, Anda benar; jelas ada input yang memungkinkan beberapa pengaturan valid,
Cristian Lupascu

Jawaban:

6

Haskell, 1007 Seribu Satu Bytes, 923 900 830 byte

Saya kebetulan telah membuat pemecah happycube, sekarang saya hanya perlu bermain golf. Mengambil penalti sepuluh byte untuk menggunakan elemen blok mewah:

import Data.List
r=reverse;z=zipWith;f=foldl1;t=take;g=t 4;d=drop;m=map
n x=t 5x:n(d 4x)
u v a b=init a++v max(last a)(b!!0):d 1b
a!b|k<- \x y->last$' ':[a|b!!x!!y>0]=(k 0<$>[0..4]):((\i->k 3(4-i):[a,a,a]++[k 1i])<$>[1..3])++[k 2<$>[4,3..0]]
p y|(e:v)<-m(g.n.cycle.m(read.pure))$"0":(lines$y),[j,k,l,x,y,r]<-x v=mapM putStrLn$f(u z)$f(z(u id))<$>z(z(!))[a,"▒█▒█",a][[e,k,e,e],[l,j,x,r],[e,y,e,e]];a=" ░  "
x(p:q)=[p:u|x<-permutations q,u@[e,g,y,k,l]<-sequence$(\c->nub$[c,r.m r$c]>>=g.m g.tails.cycle)<$>x,and$zipWith4(\a n b m->all(==1).init.d 1$z(+)(a!!n)$r$b!!m)([l,e,p,p,p,p,e]++u)[3,3,0,3,1,2,0,1,2,2,2,1](y:g:u++[y,k,k,l,g])[1,0,2,1,3,0,0,0,3,1,2,3]++z((((==1).sum.m(!!0)).).z(!!))[[p,e,g],[y,p,e],[k,p,g],[k,p,y],[l,y,e],[l,y,k],[l,g,e],[l,g,k]][[0,3,1],[0..2],[0,3,2],[1..3],[0,1,1],[3,2,2],[1,0,0],[2,3,3]]]!!0

Itu seteguk. Pemakaian:

*Main> mapM_ (\s->p s>>putStrLn"")["0010010101010101\n0010001011011010\n0101001001010010\n0010110100101101\n0010110110101101\n0010001011010101","0001110110101101\n1010010111011101\n0101010101010010\n1010001000100011\n1010001001010001\n0110010100100010","0101001011011010\n0010001000100010\n0101001011010010\n0101010101011010\n0101101001011101\n1010001001011101"]
            
     ░░░      
    ░░░░░     
     ░░░      
▒▒ ▒▒░█░▒▒ ▒█ 
 ▒▒▒█████▒▒▒█████
▒▒▒▒▒███▒▒▒▒▒███
 ▒▒▒█████▒▒▒█████
   ░█░█▒▒ ▒▒  
    ░░░░      
     ░░░░     
    ░░░░      
            

             
     ░░░░     
    ░░░░      
     ░░░░     
▒▒ ▒▒░░██  ▒█ 
▒▒▒▒█████▒▒▒▒███
 ▒▒▒▒███▒▒▒▒▒████
▒▒▒▒█████▒▒▒████
▒▒ ▒█░█░█   
     ░░░░     
    ░░░░      
     ░░░░     
            

    ░░       
    ░░░░░     
     ░░░      
    ░░░░░     
   ▒█░█░   
▒▒▒▒▒███▒▒▒▒▒███
 ▒▒▒█████▒▒▒█████
▒▒▒▒▒███▒▒▒▒▒███
  ▒██░██    
     ░░░░     
    ░░░░      
     ░░░░     
    ░░ ░░     

Beberapa contoh memiliki lebih dari satu solusi, itu sebabnya beberapa output terlihat berbeda. Tidak Disatukan:

import Data.List (nub, transpose, (\\))
import Control.Monad (guard)

newtype CubePiece = CubePiece [[Int]] deriving Eq
newtype Solution = Solution [CubePiece]

side :: Int -> CubePiece -> [Int]
side n (CubePiece c) = c!!n

corner :: Int -> CubePiece -> Int
corner n (CubePiece c) = head $ c!!n

strToCube str = CubePiece $ hs' . (\x@(a:_)->x++[a]) $ l
  where
    l = map (read.pure) str
    hs' [a] = []
    hs' x = take 5 x : hs' (drop 4 x)

orientations :: CubePiece -> [CubePiece]
orientations (CubePiece cube) = map CubePiece $ nub $ (take 4 . iterate rotate $ cube) ++
  (take 4 . iterate rotate . reverse . map reverse $ cube)
  where
  rotate (a:as) = as++[a]

sideFits ::  (CubePiece, Int) -> (CubePiece, Int) -> Bool
sideFits (c1,n1) (c2,n2) = case (zipWith (+) a b) of
  [_,1,1,1,_] -> True
  _ -> False
  where
  a = side n1 c1
  b = reverse $ side n2 c2

cornerFits :: (CubePiece, Int) -> (CubePiece, Int) -> (CubePiece, Int) -> Bool
cornerFits (c1,n1) (c2,n2) (c3,n3) = a + b + c == 1
  where
  a = corner n1 c1
  b = corner n2 c2
  c = corner n3 c3

printSolution str = putStrLn . specialUnlines . map rivi $
  [[empty,gshow '░' c2,empty,empty],[gshow '▒' c3,gshow '█'c1,gshow '▒'c4,gshow '█'c6],[empty,gshow '░'c5,empty,empty]]
  where
  Solution [c1,c2,c3,c4,c5,c6] = solve . map strToCube . lines $ str
  empty = replicate 5 "     "
  rivi = map (foldl1 specialConcat) . transpose
  specialUnlines = unlines . foldl1(\a b->init a++[zipWith max(last a)(head b)]++tail b)
  specialConcat a b
    | last a==' '=init a++b
    | otherwise = a++tail b

gshow char (CubePiece c) =
  [ map (k 0) [0..4]
  , (k 3 3) : m ++ [(k 1 1)]
  , (k 3 2) : m ++ [(k 1 2)]
  , (k 3 1) : m ++ [(k 1 3)]
  , map (k 2)[4,3..0]
  ]
  where
  k n1 n2 = if (c!!n1)!!n2 == 1 then char else ' '
  m=replicate 3 char

solve :: [CubePiece] -> Solution
solve pieces = Solution $ head $ do
  let c1' = pieces!!0
  let c1 = c1'

  c2'  <- pieces \\ [c1']
  c2   <- orientations c2'
  guard $ sideFits (c1,0) (c2,2)

  c3'  <- pieces \\ [c1',c2']
  c3   <- orientations c3'
  guard $ sideFits (c1,3) (c3,1)
  guard $ sideFits (c2,3) (c3,0)
  guard $ cornerFits (c1,0) (c2,3) (c3,1)

  c4'  <- pieces \\ [c1',c2',c3']
  c4   <- orientations c4'
  guard $ sideFits (c1,1) (c4,3)
  guard $ sideFits (c2,1) (c4,0)
  guard $ cornerFits (c1,1) (c2,2) (c4,0)
  c5' <- pieces \\ [c1',c2',c3',c4']
  c5 <- orientations c5'
  guard $ sideFits (c1,2) (c5,0)
  guard $ sideFits (c4,2) (c5,1)
  guard $ sideFits (c3,2) (c5,3)
  guard $ cornerFits (c5,0) (c1,3) (c3,2)
  guard $ cornerFits (c5,1) (c1,2) (c4,3)

  c6' <- pieces \\ [c1',c2',c3',c4',c5']
  c6 <- orientations c6'
  guard $ sideFits (c6,0) (c2,0)
  guard $ sideFits (c6,1) (c3,3)
  guard $ sideFits (c6,2) (c5,2)
  guard $ sideFits (c6,3) (c4,1)
  guard $ cornerFits (c6,0) (c4,1) (c2,1)
  guard $ cornerFits (c6,3) (c4,2) (c5,2)
  guard $ cornerFits (c6,1) (c3,0) (c2,0)
  guard $ cornerFits (c6,2) (c3,3) (c5,3)
  return $ [c1,c2,c3,c4,c5,c6]

main = mapM_ printSolution ["0010010101010101\n0010001011011010\n0101001001010010\n0010110100101101\n0010110110101101\n0010001011010101","0001110110101101\n1010010111011101\n0101010101010010\n1010001000100011\n1010001001010001\n0110010100100010","0101001011011010\n0010001000100010\n0101001011010010\n0101010101011010\n0101101001011101\n1010001001011101"]
Angs
sumber