mugil_cephalus
Advanced Member | Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору @echo off 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 BETA-TEST!!! REM Update №3 goto :init :init REM А мы такие замкнутые... cmd.exe -> setlocal /? setlocal>nul REM Меняем цвет. Честно, это не нужно, но оставил для приличия. color 07 >nul REM Устанавливаем заголовок title Minesweeper by nsinreal REM Очищаем экран. Да, в конце игры вам будет предложено сыграть заново. Ну не оставлять же это безобразие? cls REM Создаем поле. Максимальное кол-во бомб (maxbombs) - 20. Устанавливаем кол-во бомб в 0, кол-во проставленных флагов в 0. call :logo echo Generation field. Please, wait... set maxbombs=17 set bombs=0 set flags=0 REM Не собираемся ни выходить, ни умирать не выиграывать в начале игры. Хотя, мы даже игру не стартанули set die=0 set win=0 set quit=0 set started=0 set step=0 REM Собсно сам процес создания полей. mfield - поле, выводимое на экран; rfield - реальное поле for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do set mfield%%x%%y=? for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do set rfield%%x%%y=? call :genbomb call :genrfield REM Переходим к игре. goto :gamecycle REM goto:eof - это что-то типа return или end в функции/процедуре. goto:eof :gamecycle REM Так как это... батник глючит из-за особенностей cmd.exe и нежелания читать разработчиков (как и MS, так и меня) доку, то делаем финт ушами: REM Если переменная quit не равна 0, то выходим из игры. if not "%quit%"=="0" ( endlocal>nul goto:eof ) REM Очистка экрана. cls REM Вывод поля и дополнительной информации. set line0=:yx 1 2 3 4 5 6 7 8 9 yx: for /L %%y in (1,1,9) do call set line%%y=:%%y: %%mfield%%y1%% %%mfield%%y2%% %%mfield%%y3%% %%mfield%%y4%% %%mfield%%y5%% %%mfield%%y6%% %%mfield%%y7%% %%mfield%%y8%% %%mfield%%y9%% :%%y: REM Изменяем внешний вид: все нули меняем на ноль, а все воспросительные знаки (?) на точки (.). for /L %%y in (1,1,9) do call set line%%y=%%line%%y:0= %% for /L %%y in (1,1,9) do call set line%%y=%%line%%y=.%% if %flags% GTR %maxbombs% ( set line1=%line1% !!! F ) else ( set line1=%line1% F ) REM Печатаем кол-во флагов. set line1=%line1%lags: %flags% set line3=%line3% Step: %step% REM Выписываем справку if not "%die%"=="1" if not "%win%"=="1" ( set line4=%line4% Avaibled commands: set line5=%line5% h - Output this help. set line6=%line6% o [yx] - Open point yx set line7=%line7% f [yx] - Create flag on point yx set line8=%line8% n - Start new game set line9=%line9% q - Exit to Windows or console ) REM Считаем время. Странно, но если вызывать :checkstart отсюда, а не как правильно, то все работает правильно. Очень странно. call :checkstart if not "%started%"=="0" ( call :endtime set line2=%line2% Time: %gametime% ) else ( set line2=%line2% !!! Game not started yet. ) if "%die%"=="1" set line4=%line4% BOMB!!! U died. if "%win%"=="1" set line4=%line4% WIN!!! WIN!!! WIN!!! WIN!!! WIN!!! echo %line0% Bombs: %bombs% echo :---------------------------------: for /L %%y in (1,1,8) do call :echofield line%%y echo %line9% echo :---------------------------------: echo %line0% echo. REM Проверяем, выиграл ли пользователь. if not "%die%"=="1" if not "%win%"=="1" call :wincheck REM Некоторые идиотские проверочки: если мы умерли или выиграли, то выводим запрос на создание новой игры. if "%die%"=="1" ( goto:bombdie ) else if "%win%"=="1" ( goto:dowin ) else ( REM Считываем и обрабатываем данные с клавиатуры. goto inputcycle ) goto:eof :inputcycle REM См. первый комент в :gamecycle. if not "%quit%"=="0" ( endlocal>nul goto:eof ) REM Заносим в input магическую строку. Так как переменная input может и не создатся (пользователь нажал Enter), то вводим в неё изначально такой бред. REM Так легче, чем использовать IF DEFINED, который, кстати, не всегда работает. set input=0 00 set /p "input=Input: " set input=%input: =% REM Первая буква - необходимое действие. set action=%input:~0,1% REM Уходим отсюда. if "%action%"=="q" ( set quit=1 echo Good Bye. goto:eof ) REM Выводим справку. if "%action%"=="h" ( cls call :help goto:gamecycle ) REM Выводим список выигранных игр. if "%action%"=="r" if EXIST records.log ( cls type records.log pause>nul goto:gamecycle ) else ( call:errorIO2 ) if "%action%"=="n" ( endlocal>nul goto init ) REM См. первый комент в :gamecycle. if not "%quit%"=="0" ( endlocal>nul goto:eof ) REM Издеваемся над координатами. REM Честно, мне лень переписывать. set ix=0 set iy=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 ) else ( for /L %%a in (1,1,9) do if "%%a"=="%input:~1,1%" set iy=%%a REM Координаты можно ввести без "o" в начале set action=o ) REM Выводим сообщение, что пользователь - дурак. if not "%action%"=="q" if not "%action%"=="h" if not "%action%"=="o" if not "%action%"=="f" 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 ) REM Открываем точки, ставим флаги. call :checkstart if "%action%"=="o" ( call penpoint %ix% %iy% %%rfield%ix%%iy%%% %%mfield%ix%%iy%%% goto:gamecycle ) if "%action%"=="f" ( call :flagpoint %ix% %iy% %%mfield%ix%%iy%%% goto:gamecycle ) goto:eof REM Вспомогательные процедуры. REM Выводим наш опознавательный знак. :logo echo ~ echo :77~ ~=~I~ echo ~ I~?:=7= ~?~ echo I7=~ ~I~+7+?:~?7++?=~=?7= echo :=7 7II77III??,+==~~~=I=~ echo ~:=I?~7?+7II??++~=?+~:~~ echo I777:=I7I??I??++??=~:::~ echo I777 :~+I7III??++?+~~~::~= echo ~?IIII?777 777II??++=~~~~::=777II= echo ~~=?I?77777777II???+?==~~:::~7I+~~ echo ~,+?IIII7IIII???++??+~~:::~~: echo ~???~??IIII???++~::,~::::~=++~ echo ~~~+~I ??:~+??++=~,:::::~~=?+++~ echo ~:,,I===~~++,=?+====~~~:::~+=+I::::~ echo =+=======:::=~~~~:::I +?? echo ==~=======~~~~::::~:~+ ? echo ~=====~~~~~::::~~=,~?=:~ echo :+?==~~~~:+ +~~==+I+~==~ MINESWEEPER echo =+~:~~==~,~ 7~=+?I? ~,~= BY NSINREAL echo =: ~:=: ++?~~: ~ echo , 7 , ~~ echo ~ ~~ echo. goto:eof REM Пользователь дурак. :errorIO1 cls echo Error I/O: Unknown coordinates call:help goto:gamecycle REM Пользователь дважды дурак. :errorIO2 cls echo Error I/O: Unknown command call:help goto:gamecycle REM Выписываем пользователю справку. :help echo Avaibled commands: echo h - Output this help. echo o [yx] - Open point yx echo f [yx] - Create flag on point yx echo n - Start new game IF EXIST records.log echo r - Output all records echo q - Exit to Windows or console echo. echo Example: o 12 - open point with coordinates y=1, x=2 echo f 34 - create flag on point y=3, x=4 echo n - start new game echo new game - start new game echo. pause>nul goto:eof REM Генерируем бомбы. :genbomb set nx=%1 set ny=%2 set r1=%random:~-2% set r2=%random:~-2% set r1=%r1:0= % set r2=%r2:0= % set r1=%r1: =% set r2=%r2: =% set r1=%r1:~0,1% set r2=%r2:~0,1% if "%r1%"==0 goto :genbomb %nx% %ny% if "%r2%"==0 goto :genbomb %nx% %ny% if "%r1%"=="%nx%" goto :genbomb %nx% %ny% if "%r2%"=="%ny%" goto :genbomb %nx% %ny% REM Фокус с call - это преобразование имени переменной в её значение. В принципе, можно сделать "call echo %%rfield%%x%%y%%" и будет выведено содержание текущей клетки. REM Но вот if там нельзя использовать, поэтому мы делаем вызов процедуры с передачей значения переменной и её имени. call :newbomb %%rfield%r1%%r2%%% rfield%r1%%r2% REM Делаем бомбы пока их количество не будет равно максимальному. if "%bombs%" == "%maxbombs%" goto:eof goto genbomb REM Создаем бомбы, если её нету. Вызывается из :genbomb. :newbomb if not "%1"=="X" ( set %2=X set /a bombs=%bombs%+1 ) goto:eof REM В сапере обычно используются числа для обозначения кол-ва стоящих рядом бомб. Перебираем все клетки и вносим туда необходимые числа. Опять же, используем фокус с call :genrfield for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call osumfield %%x %%y %%rfield%%x%%y%% rfield%%x%%y goto:eof REM А эта процедура записывает необходимые числа в необходимые клеточки. osumfield REM %1, %2 - координаты (x, y), %3 - содержание клеточки, %4 - имя переменной. REM Если клеточка заполнена, то выходим из процедуры. if not "%3"=="?" goto:eof REM Устанавливаем координаты ближайших клеточек. set /a x1=%1 - 1 set /a y1=%2 + 1 set /a x2=%1 set /a y2=%2 + 1 set /a x3=%1 + 1 set /a y3=%2 + 1 set /a x4=%1 - 1 set /a y4=%2 set /a x5=%1 + 1 set /a y5=%2 set /a x6=%1 - 1 set /a y6=%2 - 1 set /a x7=%1 set /a y7=%2 - 1 set /a x8=%1 + 1 set /a y8=%2 - 1 REM sum - количество бомб рядом, обнуляем. set sum=0 REM Проверяем координаты: GTR больше, LSS меньше. "Массив" у нас задан координатами [1..9,1..9], а в результате предыдущих действий координаты выходили за пределы массива. if %1 GTR 1 if %2 LSS 9 call :newsum %%rfield%x1%%y1%%% if %2 LSS 9 call :newsum %%rfield%x2%%y2%%% if %1 LSS 9 if %2 LSS 9 call :newsum %%rfield%x3%%y3%%% if %1 GTR 1 call :newsum %%rfield%x4%%y4%%% if %1 LSS 9 call :newsum %%rfield%x5%%y5%%% if %1 GTR 1 if %2 GTR 1 call :newsum %%rfield%x6%%y6%%% if %2 GTR 1 call :newsum %%rfield%x7%%y7%%% if %1 LSS 9 if %2 GTR 1 call :newsum %%rfield%x8%%y8%%% set %4=%sum% goto:eof REM Так как call не воспринимает if, то используем отдельную процедуру. :newsum if "%1"=="X" set /a sum+=1 goto:eof REM Удаляем числа elrfield for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call elsumfield %%x %%y %%rfield%%x%%y%% rfield%%x%%y goto:eof elsumfield if "%3"=="?" goto:eof if "%3"=="X" goto:eof set %4=? goto:eof REM Эта процедура относится к выводу поля. Я не знаю как еще в for-цикле использовать две и более команды, кроме как использовать отдельную процедуру. :echofield REM Выводим поле. И пустую строчку. call echo %%%1%% echo :-: :-: goto:eof REM Открываем клеточку penpoint rem %1, %2 - x, y; %3 - значение клетки реального поля; %4 - значение клетки видимого поля. set /a step+=1 REM Если клетка не пуста - рассказываем пользователю много интересного, если в клетке бомба - уже поздно что-либо рассказывать. if not "%4"=="?" ( echo Point x=%1 y=%2 already opened pause>nul goto:eof ) if "%3"=="X" if not "%step%"=="1" ( set die=1 for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call set mfield%%x%%y=%%rfield%%x%%y%% goto:eof ) if "%step%"=="1" if "%3"=="X" ( set rfield%1%2=? set /a bombs-=1 call elrfield call :genbomb %1 %2 call :genrfield rem call set mfield%1%2=%%rfield%1%2%% call af %1 %2 %%rfield%1%2%% %%mfield%1%2%% ) REM А если ни то, ни другое, то пытаемся открыть эту и ближние клетки. call af %1 %2 %3 %4 goto:eof af rem %1, %2 - x, y; %3 - значение клетки реального поля; %4 - значение клетки видимого поля. REM Если клетка пуста (выход за пределы поля) или в ней бомба - уходим отсюда. if "%3"=="" goto:eof if "%3"=="X" goto:eof REM Открываем данную клетку call set mfield%1%2=%%rfield%1%2%% REM Если в данной клетке 0 бомб, то пытаемся открыть все ближние клетки. if not "%3" == "0" goto:eof REM xn, yn - координаты следующей клетки. Диагональ не проверяется. set /a xn=%1 set /a yn=%2 + 1 REM Проверка, открыта ли эта клетка на видимом поле. set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% REM Если нет - пытаемся открыть. if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% set /a xn=%1 set /a yn=%2 - 1 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% set /a xn=%1 - 1 set /a yn=%2 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% set /a xn=%1 + 1 set /a yn=%2 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% REM Диагональ. set /a xn=%1 + 1 set /a yn=%2 + 1 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% %%rfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% set /a xn=%1 + 1 set /a yn=%2 - 1 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% %%rfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% set /a xn=%1 - 1 set /a yn=%2 + 1 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% %%rfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% set /a xn=%1 - 1 set /a yn=%2 - 1 set dooaf=0 call :checkoaf %%mfield%xn%%yn%%% %%rfield%xn%%yn%%% if %dooaf%==1 call af %xn% %yn% %%rfield%xn%%yn%%% %%mfield%xn%%yn%%% goto:eof REM Проверка, открыта ли клетка на видимом поле. :checkoaf if "%1"=="?" set dooaf=1 goto:eof REM Устанавливаем флаг :flagpoint REM %1, %2 - x, y; %3 - значение клетки видимого поля REM Если в клетке уже что-то есть - рассказываем пользователю что-то интересное if not "%3"=="?" if not "%3"=="!" ( echo Point %2 %1 already opened pause>nul goto:eof ) REM Если нету флага - ставим, если есть флаг - убираем. Так же изменяем счетчик. if not "%3"=="!" ( set /a flags+=1 call set mfield%%1%%2=^! ) else ( set /a flags-=1 call set mfield%%1%%2=? ) goto:eof REM Бабах... Мы все умрем... А вот пользователь уже подорвался на какой-то бомбе. Неудачливый из него сапер. :bombdie REM :bombdie требует вывода поля (заново), поэтому используется дополнительная переменная для обозначения первого запуска :bombdie и одновременно смерти игры. if not "%die%"=="1" ( set die=1 goto :gamecycle ) REM Считываем, хочет ли пользователь еще раз сыграть игру. pause>nul echo Want to start new game? Example: yes set sgame=yes set /p sgame= set sgame=%sgame:~0,1% if "%sgame%"=="y" ( endlocal>nul goto :init ) if "%sgame%"=="Y" ( endlocal>nul goto :init ) set quit=1 goto:eof REM Проверяем, выиграл ли пользователь. :wincheck REM Если флагов больше кол-ва бомб, то пользователь ошибся! if %flags% GTR %maxbombs% goto:eof REM Если пользователь решил схитрить... то выходим. if %step%==0 goto:eof REM Считаем кол-во правильно поставленных флагов и не открытых клеточек. set nopoints=0 set rflags=0 for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call :checkfo %%mfield%%x%%y%% %%rfield%%x%%y%% REM Если количество правильно поставленных флагов равно кол-ву бомб, то юзер выиграл if %rflags%==%maxbombs% call owin REM Еще один вариант победы человека над машиной: если все флаги проставлены правильно, но стоят не везде; сумма поставленных флагов и неоткрытых клеточек равна кол-ву бомб. set /a sumpoints=%nopoints% + %rflags% if %rflags%==%flags% if %sumpoints%==%maxbombs% call owin goto:eof REM Изменяем счетчики. :checkfo if "%1"=="?" set /a nopoints+=1 if "%1"=="!" if "%2"=="X" set /a rflags+=1 goto:eof REM Мы выиграли! owin if not "%win%"=="1" ( set win=1 for /L %%x in (1,1,9) do for /L %%y in (1,1,9) do call set mfield%%x%%y=%%rfield%%x%%y%% goto :gamecycle ) pause>nul set name=%USERNAME% set /p name="Your name: " set name=%name%:%USERNAME%@%USERDOMAIN% set name=%name:|=% IF not EXIST records.log echo ----- Minesweeper' records ----- >records.log echo Name: %name% ^| Steps: %step% ^| GameTime: %gametime% ^| Flags: %rflags% ^| Not opened: %nopoints% ^| Date-Time: %date% %time% >>records.log 2>nul echo Want to start new game? Example: yes set sgame=yes set /p sgame= set sgame=%sgame:~0,1% if "%sgame%"=="y" ( endlocal>nul goto :init ) if "%sgame%"=="Y" ( endlocal>nul goto :init ) set quit=1 goto:eof REM Первая процедура для подсчета времени :checkstart if not "%started%"=="0" goto:eof 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% endlocal >nul |