@echo off REM Свои судоку на своих батниках REM Author: nsinreal REM Blog: http://nsinreal.blogspot.com REM E-Mail: nsinreal+batch@gmail.com REM XMPP: xmpp://nsinreal@jabber.ru REM License: cc-by-sa 3.0 REM Копирайты кончились, дальше идут код и комментарии. REM Стандартные процедуры обеспечения нормальной работы батников и внешней среды setlocal >nul goto init REM Выход из батника goto:eof :init cls title Sudoku by nsinreal endlocal >nul setlocal >nul set win=0 set started=0 set newgame=0 set quit=0 set started=0 REM Вызов необходимых процедур call:generatetable goto gamecycle goto:eof :generatetable REM abcdefghi - 123456789, 213456790, * (random) REM map length = 9*9=81 REM MAGIC!!! REM Для генерации судоку используется метод таблиц. REM Здесь записано 8 таблиц (не симетричных относительно горизонтали и вертикали) REM В каждой таблице находится 81 символ (9х9). Набор символов: abcdefghi REM Буквы (a, b, c, *) заменяются на цифры 9, 8, 7 в неизвестном порядке. REM Это обеспечивает 9!*8=2903040 вариаций. REM В принципе, можно еще делать горизонтальный и вертикальный флипы (*4 вариаций) REM Но мне лень. Простите. set map1=hgifeadcbfecdgbaihdbacihgefidbghcfaecahbfeidggfeadihbcecgiafbhdbidhcgefaahfebdcgi set map2=ifcdbgeaheadcihbgfhbgafedicbeaghfcdidcieabhfgghfidcaebfdhbgaicecibfedghaagehcifbd set map3=bahdfgceiigfchebdaedcbiahgfgbefdcaihhfaiebdcgdcigahfbefigacdehbahdebigfccebhgfiad set map4=gefadbhicdcbihgafeaihefcdgbbhadiefcgfgehcabdiidcgbfeahhagcedibfcbifahgedefdbgicha set map5=iadchgbfebhgfaecdicefdbiaghhiagebfcddfbicheagegcadfhibgbihfcdeaachegdibffdebiaghc set map6=hgacbdefiidbfegachcefihadbggbhdiecafafihcbgdedceagfihbbhceaifgdfigbdcheaeadgfhbic set map7=beghfdciadficagebhacheibdgffgcbheadiidbagfhecehadcigfbcbdfeaihghiegbcfadgafidhbce set map8=hcbgeaifdadichfgbegefidbachibdfcehgafgahbdeicehcaigbdfbahdgcfeidigefhcabcfebaidhg REM Выбираем необходимую таблицу. Это подфункция: :selectmap set selectedmap=%random:~-1% REM Если random выдал число 0 или 9, то возращаемся к выбору карты. if %selectedmap% == 0 goto selectmap if %selectedmap% == 9 goto selectmap REM END CYCLE :selectmap call:echoloading 1 REM Загадочный алгоритм, обеспечивающий 9!=362880 вариаций таблицы судоку. for /L %%i in (1,1,9) do set replmap%%i=%%i set replcounter=0 REM И это тоже подфункция: :replcycle set temp1=%random:~-1% if %temp1% == 0 goto replcycle set temp2=%random:~-1% if %temp2% == 0 goto replcycle set /a replcounter+=1 call set temp=%%replmap%temp1%%% call set replmap%temp1%=%%replmap%temp2%%% call set replmap%temp2%=%temp% set "temp=" set /a level= %replcounter% / 63 + 1 set /a replmod = %replcounter% %% 63 if %replmod% == 0 call:echoloading %level% if %replcounter% LSS 504 goto replcycle REM END CYCLE :replcycle REM Генерируем таблицу. Тоже загадочный алгоритм. REM Но этот алгоритм должен быть более понятен, чем прошлый. call set map=%%map%selectedmap%%% call :replacestr map a %replmap1% call :replacestr map b %replmap2% call :replacestr map c %replmap3% call :replacestr map d %replmap4% call :replacestr map e %replmap5% call :replacestr map f %replmap6% call :replacestr map g %replmap7% call :replacestr map h %replmap8% call :replacestr map i %replmap9% for /L %%i in (1,1,9) do for /L %%j in (1,1,9) do call :settable %%i %%j REM Магическая функция для удаления set delcounter=0 :deletecycle set x=%random:~-1% if %x% == 0 goto deletecycle set y=%random:~-1% if %y% == 0 goto deletecycle call :checkisempty %%field%x%%y%%% if %isempty% == 1 goto deletecycle set /a delcounter+=1 set field%x%%y%=. if %delcounter% LSS 40 goto deletecycle REM END CYCLE :deletecycle for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call set mfield%%x%%y=%%field%%x%%y%% REM Конец функции генерации таблицы goto:eof :checkisempty REM Функция проверки: является ли клетка пуста? set isempty=0 if %1 == . set isempty=1 goto:eof :replacestr REM Функция замены строки. call set temp=%%%1%% call set temp=%%temp:%2=%3%% set %1=%temp% goto:eof :settable REM Функция генерации таблицы из готовой карты (map). set x=%1 set y=%2 set /a p=%1*9+%2-10 call set p=%%map:~%p%,1%% set field%x%%y%=%p% goto:eof :gamecycle if %quit%==1 exit /B REM Вывод судоку, прочие нехорошие процедуры cls call:countremain call:checkstart call:echotable call:checkwin set newgame=0 if %win%==1 call:win if %newgame%==1 goto init if %quit%==1 exit /B cls call:echotable echo. REM Заносим в input магическую строку. Так как переменная input может и не создатся (пользователь нажал Enter), то вводим в неё изначально такой бред. REM Так легче, чем использовать IF DEFINED, который, кстати, не всегда работает. set input=0 000 set action=0 set /p "input=Input: " REM Удаляем пробелы и ненужную фигню set input=%input: =% set input=%input:"=% REM Первая буква - необходимое действие. set action=%input:~0,1% REM Выходим из игры. if "%action%"=="q" ( set quit=1 echo Good Bye. exit /B ) REM Выписываем справку. if "%action%"=="h" ( cls call :help goto gamecycle ) REM Выводим список выигранных игр. if "%action%"=="r" if EXIST records.log ( cls more records.log pause>nul cls goto gamecycle ) else ( call:errorIO2 ) if "%action%"=="n" ( goto init ) REM Издеваемся над координатами. REM Честно, мне было лень писать все заново и я это стырил с minesweeper'а. REM Правда пришлось подправить маленькие моменты set ix=0 set iy=0 set in=0 for /L %%a in (1,1,9) do if "%%a"=="%input:~0,1%" set ix=%%a if %ix%==0 ( for /L %%a in (1,1,9) do if "%%a"=="%input:~1,1%" set ix=%%a for /L %%a in (1,1,9) do if "%%a"=="%input:~2,1%" set iy=%%a for /L %%a in (1,1,9) do if "%%a"=="%input:~3,1%" set in=%%a ) else ( for /L %%a in (1,1,9) do if "%%a"=="%input:~1,1%" set iy=%%a for /L %%a in (1,1,9) do if "%%a"=="%input:~2,1%" set in=%%a REM Координаты можно ввести без "s" в начале set action=s ) REM Выводим сообщение, что пользователь - дурак. if not "%action%"=="q" if not "%action%"=="h" if not "%action%"=="s" if not "%action%"=="d" if not "%action%"=="n" if not "%action%"=="r" call:errorIO2 REM Выводим еще одно сообщение, что пользователь - дурак. if "%ix%"=="0" ( call :errorIO1 goto gamecycle ) if "%iy%"=="0" ( call :errorIO1 goto gamecycle ) if "%action%"=="d" ( call :delpoint %ix% %iy% %%field%ix%%iy%%% %%mfield%ix%%iy%%% goto gamecycle ) if "%in%"=="0" ( call :errorIO1 goto gamecycle ) if "%action%"=="s" ( call :setpoint %ix% %iy% %in% %%field%ix%%iy%%% %%mfield%ix%%iy%%% goto gamecycle ) goto gamecycle goto:eof :countremain REM Подсчет незаполненных клеток set rcounter=0 for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call :addcountr %%mfield%%x%%y%% goto:eof :addcountr if %1 == . set /a rcounter+=1 goto:eof :delpoint REM %1, %2 - x, y; %3 - значение клетки реального поля; %4 - значение клетки видимого поля. if %3 == %4 if not %4 == . ( cls call:echotable echo U can't clear point (%1;%2^)=%3 pause>nul cls ) if not %3 == %4 if not %4 == . set mfield%1%2=. if %4 == . ( cls call:echotable echo U can't clear point (%1;%2^), couz it's empty. pause>nul cls ) goto:eof :setpoint REM %1, %2 - x, y; %3 - n, %4 - значение клетки реального поля; %5 - значение клетки видимого поля. if %4 == %5 if not %5 == . ( cls call:echotable echo U can't set point (%1;%2^)=%4 to %3 pause>nul cls ) else ( set mfield%1%2=%3 ) if not %4 == %5 if not %5 == . ( cls call:echotable echo U can't set point (%1;%2^) to %3. U must delete point: d%1%2 pause>nul cls ) goto:eof REM Пользователь дурак. :errorIO1 if %quit%==1 exit /B cls echo Error I/O: Unknown coordinates or number call:help goto gamecycle REM Пользователь дважды дурак. :errorIO2 if %quit%==1 exit /B cls echo Error I/O: Unknown command call:help goto gamecycle REM Выписываем пользователю справку. :help if %quit%==1 exit /B echo Avaibled commands: echo h - Output this help. echo s [xyn] - Set number n to point xy echo d [xy] - Delete number from point xy echo n - Start new game IF EXIST records.log echo r - Output all records echo q - Exit to Windows or console echo. echo Example: s 12 3 - set number 3 to point with coordinates x=1, y=2 echo n - start new game echo new game - start new game echo. pause>nul cls goto:eof :checkwin set win=1 set "errorin=" REM В батниках гораздо проще сделать и посчитать все руками. Кстати, большая программа лучше смотрится. Солиднее что-ли. REM Если добавить еще загадочные алгоритмы (+/- 100 грамм), то это программу вообще можно сдавать заказчику. REM Проверка: строки set "templine=%mfield11%%mfield12%%mfield13%%mfield14%%mfield15%%mfield16%%mfield17%%mfield18%%mfield19%" & set "temperror=line 1" & call:checktemp set "templine=%mfield21%%mfield22%%mfield23%%mfield24%%mfield25%%mfield26%%mfield27%%mfield28%%mfield29%" & set "temperror=line 2" & call:checktemp set "templine=%mfield31%%mfield32%%mfield33%%mfield34%%mfield35%%mfield36%%mfield37%%mfield38%%mfield39%" & set "temperror=line 3" & call:checktemp set "templine=%mfield41%%mfield42%%mfield43%%mfield44%%mfield45%%mfield46%%mfield47%%mfield48%%mfield49%" & set "temperror=line 4" & call:checktemp set "templine=%mfield51%%mfield52%%mfield53%%mfield54%%mfield55%%mfield56%%mfield57%%mfield58%%mfield59%" & set "temperror=line 5" & call:checktemp set "templine=%mfield61%%mfield62%%mfield63%%mfield64%%mfield65%%mfield66%%mfield67%%mfield68%%mfield69%" & set "temperror=line 6" & call:checktemp set "templine=%mfield71%%mfield72%%mfield73%%mfield74%%mfield75%%mfield76%%mfield77%%mfield78%%mfield79%" & set "temperror=line 7" & call:checktemp set "templine=%mfield81%%mfield82%%mfield83%%mfield84%%mfield85%%mfield86%%mfield87%%mfield88%%mfield89%" & set "temperror=line 8" & call:checktemp set "templine=%mfield91%%mfield92%%mfield93%%mfield94%%mfield95%%mfield96%%mfield97%%mfield98%%mfield99%" & set "temperror=line 9" & call:checktemp REM Проверка: столбцы set "templine=%mfield11%%mfield21%%mfield31%%mfield41%%mfield51%%mfield61%%mfield71%%mfield81%%mfield91%" & set "temperror=column 1" & call:checktemp set "templine=%mfield12%%mfield22%%mfield32%%mfield42%%mfield52%%mfield62%%mfield72%%mfield82%%mfield92%" & set "temperror=column 2" & call:checktemp set "templine=%mfield13%%mfield23%%mfield33%%mfield43%%mfield53%%mfield63%%mfield73%%mfield83%%mfield93%" & set "temperror=column 3" & call:checktemp set "templine=%mfield14%%mfield24%%mfield34%%mfield44%%mfield54%%mfield64%%mfield74%%mfield84%%mfield94%" & set "temperror=column 4" & call:checktemp set "templine=%mfield15%%mfield25%%mfield35%%mfield45%%mfield55%%mfield65%%mfield75%%mfield85%%mfield95%" & set "temperror=column 5" & call:checktemp set "templine=%mfield16%%mfield26%%mfield36%%mfield46%%mfield56%%mfield66%%mfield76%%mfield86%%mfield96%" & set "temperror=column 6" & call:checktemp set "templine=%mfield17%%mfield27%%mfield37%%mfield47%%mfield57%%mfield67%%mfield77%%mfield87%%mfield97%" & set "temperror=column 7" & call:checktemp set "templine=%mfield18%%mfield28%%mfield38%%mfield48%%mfield58%%mfield68%%mfield78%%mfield88%%mfield98%" & set "temperror=column 8" & call:checktemp set "templine=%mfield19%%mfield29%%mfield39%%mfield49%%mfield59%%mfield69%%mfield79%%mfield89%%mfield99%" & set "temperror=column 9" & call:checktemp REM Проверка: квадраты set "templine=%mfield11%%mfield12%%mfield13%%mfield21%%mfield22%%mfield23%%mfield31%%mfield32%%mfield33%" & set "temperror=square 1" & call:checktemp set "templine=%mfield14%%mfield15%%mfield16%%mfield24%%mfield25%%mfield26%%mfield34%%mfield35%%mfield36%" & set "temperror=square 2" & call:checktemp set "templine=%mfield17%%mfield18%%mfield19%%mfield27%%mfield28%%mfield29%%mfield37%%mfield38%%mfield39%" & set "temperror=square 3" & call:checktemp set "templine=%mfield41%%mfield42%%mfield43%%mfield51%%mfield52%%mfield53%%mfield61%%mfield62%%mfield63%" & set "temperror=square 4" & call:checktemp set "templine=%mfield44%%mfield45%%mfield46%%mfield54%%mfield55%%mfield56%%mfield64%%mfield65%%mfield66%" & set "temperror=square 5" & call:checktemp set "templine=%mfield47%%mfield48%%mfield49%%mfield57%%mfield58%%mfield59%%mfield67%%mfield68%%mfield69%" & set "temperror=square 6" & call:checktemp set "templine=%mfield71%%mfield72%%mfield73%%mfield81%%mfield82%%mfield83%%mfield91%%mfield92%%mfield93%" & set "temperror=square 7" & call:checktemp set "templine=%mfield74%%mfield75%%mfield76%%mfield84%%mfield85%%mfield86%%mfield94%%mfield95%%mfield96%" & set "temperror=square 8" & call:checktemp set "templine=%mfield77%%mfield78%%mfield79%%mfield87%%mfield88%%mfield89%%mfield97%%mfield98%%mfield99%" & set "temperror=square 9" & call:checktemp goto:eof :checktemp REM Функция проверяет правильность заполнения строки, столбца или квадрата. set templine=%templine:1=1+% set templine=%templine:2=2+% set templine=%templine:3=3+% set templine=%templine:4=4+% set templine=%templine:5=5+% set templine=%templine:6=6+% set templine=%templine:7=7+% set templine=%templine:8=8+% set templine=%templine:9=9+% set templine=%templine:~0,17% if %templine%==%templine:.=% ( REM Эдакий финт ушами: в самой ветке сравнения (if) работа с переменными превращается в черт знает что REM А вот если вызвать процедуру, то все хорошо. Вроде. call:checktemp2 ) else ( set win=0 ) goto:eof :checktemp2 set /a temp1=%templine% set /a temp2=%templine:+=*% REM Основная идея: не считать кол-во двоек, троек и прочих чисел, а просто сложить и умножить, после проверить результат. if not %temp1%==45 if not %temp2%==362880 ( set win=0 set errorin=%temperror% ) set temp1=0 set temp2=0 goto:eof :win echo WIN!!! WIN!!! WIN!!! WIN!!! WIN!!! Your name: set name=%USERNAME% set /p name= set name=%name:"=% set name=%name:|=% set tempname=%name% set name=%name%:%USERNAME%@%USERDOMAIN% IF not EXIST records.log echo ----- Sudoku' records ----- >records.log echo Name: %name% ^| GameTime: %gametime% ^| Date-Time: %date% %time% >>records.log 2>nul cls call:echotable echo Want to play again, %tempname%? Example: yes set sgame=yes set /p sgame= set sgame=%sgame:~0,1% set newgame=0 set quit=1 if "%sgame%"=="y" ( set newgame=1 set quit=0 ) if "%sgame%"=="Y" ( set newgame=1 set quit=0 ) if %quit%==1 exit /B goto:eof :echotable REM Функция вывода судоку echo +-+-+-+ +-+-+-+ +-+-+-+ set "line1=^|%mfield11%^|%mfield12%^|%mfield13%^| ^|%mfield14%^|%mfield15%^|%mfield16%^| ^|%mfield17%^|%mfield18%^|%mfield19%^|" echo 1 %line1% 1 Time: %gametime% echo +-+-+-+ +-+-+-+ +-+-+-+ set "line2=^|%mfield21%^|%mfield22%^|%mfield23%^| ^|%mfield24%^|%mfield25%^|%mfield26%^| ^|%mfield27%^|%mfield28%^|%mfield29%^|" echo 2 %line2% 2 Remaing: %rcounter% echo +-+-+-+ +-+-+-+ +-+-+-+ set "line3=^|%mfield31%^|%mfield32%^|%mfield33%^| ^|%mfield34%^|%mfield35%^|%mfield36%^| ^|%mfield37%^|%mfield38%^|%mfield39%^|" if "%errorin%"=="" ( echo 3 %line3% 3 ) else ( echo 3 %line3% 3 Error in %errorin% ) echo +-+-+-+ +-+-+-+ +-+-+-+ echo 1 2 3 4 5 6 7 8 9 echo +-+-+-+ +-+-+-+ +-+-+-+ set "line4=^|%mfield41%^|%mfield42%^|%mfield43%^| ^|%mfield44%^|%mfield45%^|%mfield46%^| ^|%mfield47%^|%mfield48%^|%mfield49%^|" echo 4 %line4% 4 echo +-+-+-+ +-+-+-+ +-+-+-+ set "line5=^|%mfield51%^|%mfield52%^|%mfield53%^| ^|%mfield54%^|%mfield55%^|%mfield56%^| ^|%mfield57%^|%mfield58%^|%mfield59%^|" echo 5 %line5% 5 echo +-+-+-+ +-+-+-+ +-+-+-+ set "line6=^|%mfield61%^|%mfield62%^|%mfield63%^| ^|%mfield64%^|%mfield65%^|%mfield66%^| ^|%mfield67%^|%mfield68%^|%mfield69%^|" echo 6 %line6% 6 echo +-+-+-+ +-+-+-+ +-+-+-+ echo 1 2 3 4 5 6 7 8 9 echo +-+-+-+ +-+-+-+ +-+-+-+ set "line7=^|%mfield71%^|%mfield72%^|%mfield73%^| ^|%mfield74%^|%mfield75%^|%mfield76%^| ^|%mfield77%^|%mfield78%^|%mfield79%^|" echo 7 %line7% 7 echo +-+-+-+ +-+-+-+ +-+-+-+ set "line8=^|%mfield81%^|%mfield82%^|%mfield83%^| ^|%mfield84%^|%mfield85%^|%mfield86%^| ^|%mfield87%^|%mfield88%^|%mfield89%^|" echo 8 %line8% 8 echo +-+-+-+ +-+-+-+ +-+-+-+ set "line9=^|%mfield91%^|%mfield92%^|%mfield93%^| ^|%mfield94%^|%mfield95%^|%mfield96%^| ^|%mfield97%^|%mfield98%^|%mfield99%^|" echo 9 %line9% 9 echo +-+-+-+ +-+-+-+ +-+-+-+ goto:eof :echoloading REM Вывод прогресса генерации поля. cls for /L %%i in (1,1,9) do set "s%%i= " for /L %%i in (1,1,%1) do set s%%i=%%i set "leftspace= " for /L %%i in (1,1,6) do echo. echo %leftspace%+-----+-----+-----+ echo %leftspace%^| ^| ^| ^| echo %leftspace%^| %s1% ^| %s2% ^| %s3% ^| echo %leftspace%^| ^| ^| ^| echo %leftspace%+-----+-----+-----+ echo %leftspace%^| ^| ^| ^| echo %leftspace%^| %s4% ^| %s5% ^| %s6% ^| echo %leftspace%^| ^| ^| ^| echo %leftspace%+-----+-----+-----+ echo %leftspace%^| ^| ^| ^| echo %leftspace%^| %s7% ^| %s8% ^| %s9% ^| echo %leftspace%^| ^| ^| ^| echo %leftspace%+-----+-----+-----+ echo. echo. echo %leftspace% Sudoku by nsinreal goto:eof :checkstart if %started%==0 ( set stime=%time% set sdate=%date:~0,2% set started=1 ) call :endtime goto:eof REM Вторая (она же основная) процедура для подсчета времени :endtime REM Издеваемся над временем. set rtime=%time% set stime=%stime::= % set stime= %stime:,= % set stime=%stime: 0= % set rdate=%date:~0,2% set rtime=%rtime::= % set rtime= %rtime:,= % set rtime=%rtime: 0= % REM В %stime% и %rtime% прописано четыре числа (разделители - пробелы): часы, минуты, секунды, миллисекунды set counter=4 for /L %%a in (1,1,%counter%) do set s%counter%= for /L %%a in (1,1,%counter%) do set r%counter%= REM Начинаем обрабатывать строки stime и rtime с конца. :looptime for /F "tokens=%counter%" %%a in ("%stime%") do set s%counter%=%%a for /F "tokens=%counter%" %%a in ("%rtime%") do set r%counter%=%%a set /a counter-=1 If %counter% GTR 0 goto looptime REM Выражаем время одним числом set /a s=%s4% + %s3%*100 + %s2%*6000 + %s1%*360000 set /a r=%r4% + %r3%*100 + %r2%*6000 + %r1%*360000 REM Учитываем разницу в днях. if %rdate% GTR %sdate% set /a r+=8640000*(%rdate%-%sdate%) REM Считаем разницу во времени. set /a d=%r% - %s% set /a d1=%d% / 360000 set /a td2=%d% %% 360000 set /a d2=%td2% / 6000 set /a td3=%td2% %% 6000 set /a d3=%td3% / 100 set /a td4=%td3% %% 100 set /a d4=%td4% if %d1% LSS 10 set d1=0%d1% if %d2% LSS 10 set d2=0%d2% if %d3% LSS 10 set d3=0%d3% if %d4% LSS 10 set d4=0%d4% REM Устанавливаем прошедшее время. set gametime=%d1%:%d2%:%d3%.%d4% goto:eof