Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: 8th: Sudokun ratkaisija graafisella käyttöliittymällä

jalski [14.02.2021 21:37:30]

#

Alla simppeli graafisella käyttöliittymällä varustettu Sudokun ratkaisija 8th ohjelmointikielellä. Ulkoasultaan ohjelma näyttää tältä.

Edit: Lisäsin yksinkertaisen parserin Sudokun lukemiseen datatiedostosta.

\
\ loader.8th
\
needs file/getc


ns?

ns: loader

[ 10, 13, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 ] constant chars

: init-vars
  1 "column" t:!
  1 "line" t:!
  0 "cell" t:!
  a:new "data" t:! ;

: column+=1
  "column" t:@ n:1+
  "column" t:!  ;

: cell+=1
  "cell" t:@ n:1+
  "cell" t:! ;

: data@
  "data" t:@ ;

: cr
  column+=1 ;

: newline
  "line" t:@ n:1+
  "line" t:!
  1 "column" t:! ;

: zero
  data@ 0 a:push drop
  column+=1
  cell+=1 ;

: one
  data@ 1 a:push drop
  column+=1
  cell+=1 ;

: two
  data@ 2 a:push drop
  column+=1
  cell+=1 ;

: three
  data@ 3 a:push drop
  column+=1
  cell+=1 ;

: four
  data@ 4 a:push drop
  column+=1
  cell+=1 ;

: five
  data@ 5 a:push drop
  column+=1
  cell+=1 ;

: six
  data@ 6 a:push drop
  column+=1
  cell+=1 ;

: seven
  data@ 7 a:push drop
  column+=1
  cell+=1 ;

: eight
  data@ 8 a:push drop
  column+=1
  cell+=1 ;

: nine
  data@ 9 a:push drop
  column+=1
  cell+=1 ;

[ ' newline , ' cr , ' zero , ' zero , ' one , ' two ,
  ' three , ' four , ' five , ' six , ' seven , ' eight , ' nine ] constant actions

: load  \ s -- a
  init-vars
  f:open
  repeat
    f:getc null? not if
      chars swap ' n:= a:indexof nip
      null? if
        1 "column" t:@ "line" t:@ "Impossible character on line: %d at column: %d" s:strfmt t:err!
        2drop drop null false ;;
      then
      actions caseof
      "cell" t:@ 81 n:> if
        2 "Can't fit to the Sudoku grid." t:err!
        2drop null false ;;
      then
    else
      drop
    then
    f:eof? not
  while! drop
  "cell" t:@ 81 n:= not if
    3 "Not enough values to fill the Sudoku grid." t:err!
    null false
  else
    0 "No error, Sudoku loaded!" t:err!
    "data" t:@ true
  then ;

ns
\
\ solver.8th
\
needs array/each-slice


ns?

ns: solver

[  00, 00, 00, 03, 03, 03, 06, 06, 06,
   00, 00, 00, 03, 03, 03, 06, 06, 06,
   00, 00, 00, 03, 03, 03, 06, 06, 06,
   27, 27, 27, 30, 30, 30, 33, 33, 33,
   27, 27, 27, 30, 30, 30, 33, 33, 33,
   27, 27, 27, 30, 30, 30, 33, 33, 33,
   54, 54, 54, 57, 57, 57, 60, 60, 60,
   54, 54, 54, 57, 57, 57, 60, 60, 60,
   54, 54, 54, 57, 57, 57, 60, 60, 60 ] constant top-left-cell

\ Bit number presentations
a:new 2 b:new b:clear a:push ( 2 b:new b:clear swap 1 b:bit! a:push ) 0 8 loop constant posbit

: posbit?  \ n -- b
  posbit swap a:@ nip const ;

: search  \ b -- n
  null swap
  (
    dup -rot b:bit@ if
      rot drop break
    else
      nip
    then
  ) 0 8 loop swap ;

: b-or  \ b b -- b
  ' n:bor b:op ;

: b-and  \ b b -- b
  ' n:band b:op ;

: b-xor  \ b b -- b
  b:xor
  [ xff, x01 ] b:new
  b-and ;

: b-not  \ b -- b
  xff b:xor
  [ xff, x01 ] b:new
  b-and ;

: b-any  \ a -- b
  ' b-or 0 posbit? a:reduce ;

: row \ a row -- a
  9 n:* 9 a:slice ;

: col  \ a col -- a
  -1 9 a:slice+ ;

: -rot9+
  -rot 9 n:+ ;

\ For testing sub boards
: sub  \ a n -- a
  top-left-cell swap a:@ nip over over 3 a:slice
  -rot9+ 2dup 3 a:slice
  -rot9+ 3 a:slice
  a:+ a:+ ;

\ Possible numbers for a cell
: candidates?  \ n -- s
  dup dup 9 n:/ n:int swap 9 n:mod \ row col
  "solver-board" t:@ swap col b-any
  "solver-board" t:@ rot row b-any
  b-or
  "solver-board" t:@ rot sub b-any
  b-or
  b-not ;

: num-of-candidates?  \ b -- n
  0 swap ( b:bit@ rot n:+ swap ) 0 8 loop drop ;

\ If found:     -- n T
\ If not found: -- T
: find-free-cell
  a:new "solver-board" t:@
  (
    tuck a:@ 0 posbit? b:= if
      -rot a:push swap
    else
      nip
    then
  ) 0 80 loop swap nip

  a:len 0 n:= not if
    ( candidates? num-of-candidates? swap candidates? num-of-candidates? n:cmp ) a:sort
    a:pop nip true
  else
    drop
    false
  then ;

: check
  true
  "solver-board" t:@
  ( dup -rot a:@ 0 posbit? b:= not if
      over a:@ >r over 0 posbit? a:! over candidates? r@ b-and 0 posbit? b:= not if
        swap r> a:!
      else
        swap r> a:!
        nip
        false swap
        break
      then
    else
      nip
    then ) 0 80 loop drop ;

: validate
  true
  "solver-board" t:@
  ( dup -rot a:@ swap 2 pick 0 posbit? a:! 2 pick candidates? 2 pick b:= if
      -rot a:!
    else
      2drop drop
      false swap
      break
    then ) 0 80 loop drop ;

: solve
  "solver-board" t:!
  check not if
    false ;;
  then
  a:new "history" t:!
  repeat
    find-free-cell if
      dup candidates?
      repeat
        search null? if
          drop "solver-board" t:@ -rot a:! drop
          "history" t:@ a:len 0 n:= if
            drop false ;;
          then
          a:pop nip
          a:open
        else
          n:1+ posbit?
          dup
          "solver-board" t:@ 4 pick rot a:! drop
          b-xor
          2 a:close
          "history" t:@ swap a:push drop
          break
        then
      again
    else
      validate
      break
    then
  again ;

ns
\
\ sudoku.8th
\
needs nk/gui
needs nk/widgets
needs nk/keyboard
needs gui/filebrowser

libbin font/Roboto-Regular.ttf

"loader.8th" f:include
"solver.8th" f:include

: secs
  d:ticks d:ticks/sec n:/ ;

a:new ( 0 solver:posbit? a:push ) 81 times var, gui-board

: new-win
  {
    name: "main",
    wide: 460,
    high: 560,
    resizable: false,
    fonts: {
      f1: {
        font: @font/Roboto-Regular.ttf
      }
    },
    fontheight: 24,
    font: "f1",
    title: "Sudoku Solver"
  }
  nk:win ;

: centered \ rect s font  --  rect' s font
  2dup nk:measure-font swap 2 a:close >r
  rot nk:rect-center nip r@ \ s font p1 dim
  ( -2 n:/ ) a:map ' n:+ a:2map
  r> a:+ -rot ;

: center-rect  \ r1 r2 -- r3
  nk:rect>size >r
  dup nk:rect>pos
  swap
  nk:rect>size r@
  ( n:- 2 n:/ ) a:2map
  ' n:+ a:2map
  r> a:+ ;

[ @scan:UP , @scan:DOWN , @scan:LEFT , @scan:RIGHT , @scan:0 , @scan:1 , @scan:2 , @scan:3 , @scan:4 , @scan:5 , @scan:6 , @scan:7 , @scan:8 , @scan:9 ] constant NUMBERKEYS

: key-state-changed?   \  s a -- a
  nk:scancode?
  ( if 1 else 0 then ) a:map over nk:get over ?:
  rot 2 pick nk:set
  ( n:cmp ) a:2map ;

: *9
  9 n:* ;

: /9
  over 9 n:/ n:int ;

: 9mod+
  9 n:mod n:+ ;

: rotdropswap
  rot drop swap ;

: up
  /9 n:1- 0 8 n:clamp 9 n:*
  2 pick 9mod+
  rotdropswap ;

: down
  /9 n:1+ 0 8 n:clamp 9 n:*
  2 pick 9mod+
  rotdropswap ;

: left
  /9 *9
  2 pick 9 n:mod n:1- 0 8 n:clamp n:+
  rotdropswap ;

: right
  /9 *9
  2 pick 9 n:mod n:1+ 0 8 n:clamp n:+
  rotdropswap ;

: number?  \ -- n | null
  null "keystates" NUMBERKEYS key-state-changed?
  (
    swap a:pop 1 n:= if
      break rot drop
    else
      nip
    then
  ) 4 2 pick a:len nip n:1- loop-
  over null? if
    drop
    (
      1 n:= if
        [ ' up , ' down , ' left , ' right ] caseof
      else
        drop
      then
    ) a:each drop
  else
   2drop
    4 n:-
  then ;

: cell-hovered-select?  \ n1 n2 rect -- n
  dup [ 0, 0, -8, -8 ] ' n:+ a:2map center-rect
  dup nk:hovered? if
    dup dup 0 1 "black" nk:stroke-rect
    nk:BUTTON_LEFT swap false nk:clicked? if
      -rot over null? if
        drop nip dup rot
      else
        over n:= not if
          nip dup
        else
          drop null swap
        then
        rot
      then
      ' noop nk:do
    then
  then
  -rot over null? not if
    n:= if
      swap 0 3 "black" nk:stroke-rect
    else
      nip
    then
  else
    2drop nip
  then ;

: generate-grid  \ w h xoffs yoffs -- a
  a:new ( >r ( 5 pick n:* 3 pick n:+ r@ 5 pick n:* 3 pick n:+ 6 pick 6 pick 4 a:close a:push ) 0 8 loop rdrop ) 0 8 loop
  nip nip nip nip ;

var solver-task-id

"" var, message
true var, input-enabled

50 constant CELL-WIDTH
50 constant CELL-HEIGHT
5 constant X-OFFS
100 constant Y-OFFS

var start-time

CELL-WIDTH CELL-HEIGHT X-OFFS Y-OFFS generate-grid constant SUDOKU-GRID

: sudoku-grid  \ n | null -- n | null
  SUDOKU-GRID
  ( dup 0 "white" nk:fill-rect
    dup 0 1 "black" nk:stroke-rect
    dup 2 pick gui-board @ swap a:@ nip solver:search nip null? not if        \ if bit is set
      [ "1","2", "3", "4", "5", "6", "7", "8", "9" ] swap caseof
    else \ no bits set
      drop ""
    then
    "f1" centered "white" "black" nk:draw-text
    input-enabled @ if    \ input is enabled if solver task is not running
      cell-hovered-select?
    else
      2drop
    then
  ) a:each drop ;

: draw-subgrid-borders   \ w h xoffs yoffs --
  3 pick 3 n:* 2 pick n:+ over 2 a:close dup 0 6 pick 9 n:* 2 a:close ' n:+ a:2map 3 "black" nk:stroke-line
  3 pick 6 n:* 2 pick n:+ over 2 a:close dup 0 6 pick 9 n:* 2 a:close ' n:+ a:2map 3 "black" nk:stroke-line
  over 4 pick  3 n:* 2 pick n:+ 2 a:close dup 4 pick 9 n:* 0 2 a:close ' n:+ a:2map 3 "black" nk:stroke-line
  over 4 pick  6 n:* 2 pick n:+ 2 a:close dup 4 pick 9 n:* 0 2 a:close ' n:+ a:2map 3 "black" nk:stroke-line
  2drop 2drop ;

: solver-ok
  solver-task-id @ t:result nip const gui-board !
  secs start-time @ n:- "Sudoku solved in %f seconds!" s:strfmt message !
  true input-enabled ! ;

: solver-fail
  "No solution!" message !
  true input-enabled ! ;

: solver-task
  solver:solve if
   "solver-board" t:@
    ' solver-ok nk:do
  else
    ' solver-fail nk:do
  then ;

: start-solver-task
  false input-enabled !
  "selected" null nk:set
  "Working..." message !
  secs start-time !
  gui-board @ const 1 ' solver-task t:task-n solver-task-id ! ;

: clear-board
  gui-board @ a:clear ( 0 solver:posbit? a:push ) 81 times drop ;

: main-render
  {
    title: "test",
    padding: [4,4],
    flags: [ @nk:WINDOW_NO_SCROLLBAR ],

    \ for the filebrowser
    chosen: null,
    showing: false,
    restrict: false,
    fb: {
      root: @getcwd ,
      rowheight: 24 ,
      multi: false ,
      filter: ["*.txt"]
    }
  }
  nk:begin
    "showing" nk:get input-enabled @ and if
      nk:win-high 1 nk:layout-row-dynamic
        ["fb","restrict"] get a:open if "top" getcwd m:! then g:filebrowser null? if drop else
          if
            \ user chose something, an array is on TOS
            a:len 0 n:= not if
              a:pop dup "chosen" swap set
              loader:load if
                ( solver:posbit? a:push ) a:each! drop gui-board !
                "Sudoku loaded succesfully!" message !
              else
                drop
                t:err? "msg" m:@ nip message !
              then
            else
              drop
            then
          else
            "chosen" null nk:set
          then
          "showing" false nk:set
        then
    else
      nk:get-row-height 3 nk:layout-row-dynamic
        "Load" ( input-enabled @ if "showing" true nk:set then ) nk:button-label
        "Clear" ( input-enabled @ if clear-board "" message ! then ) nk:button-label
        "Solve" ( input-enabled @ if start-solver-task then ) nk:button-label
      nk:get-row-height 1 nk:layout-row-dynamic
        message @ 50 nk:EDIT_SIMPLE nk:EDIT_READ_ONLY n:bor nk:PLUGIN_FILTER_DEFAULT nk:edit-string 2drop
      "selected" nk:get sudoku-grid "selected" swap nk:set
      CELL-WIDTH CELL-HEIGHT X-OFFS Y-OFFS draw-subgrid-borders
      "selected" nk:get null? not if
        number? null? not input-enabled @ and if
          solver:posbit? gui-board @ -rot a:! drop
          ' noop nk:do
        else
          drop
          dup "selected" nk:get n:= not if
            "selected" swap nk:set
            ' noop nk:do
          else
            drop
          then
        then
      else
        drop
      then
    then
  nk:end ;

: app:main
  new-win ' main-render -1 nk:render-loop ;

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta