Page 1 of 1

Minesweeper, final version 29-02-2019

Posted: Fri Mar 29, 2019 10:58 am
by Henko
Added:
- score table & file
- algorithm to detect 'white space'
E2ECE329-9A0F-4D38-9BB0-DC2C326889E8.png
E2ECE329-9A0F-4D38-9BB0-DC2C326889E8.png (418.11 KiB) Viewed 4905 times

Code: Select all

' minesweeper, version 29-03-2019 (Microsoft game)
'
init_prog()

do slowdown
  for i=0 to nb-1
    for j=0 to nb-1
      b$=i&"-"&j
      if bp(b$) then
        w(i,j)=0 ! res=m(i,j)
        if res=-1 then ! beep ! lose() ! stop ! end if
        if res>0 then button b$ text res else cleanup(i,j)
        if check_win() then ! beep ! stop ! end if
        end if
      next j
    next i
  until forever
end

def init_prog()
graphics ! graphics clear .8,.8,.8
draw color 0,0,0 ! draw size 3 ! fill color .8,.8,.8
get screen size .sw,.sh ! .xc=.sw/2
.f$="cg#76zk"
if file_exists(.f$) then show_scores()
nb=select_board_size() ! .nb=nb
dim .m(nb,nb),.w(nb,nb)
for i=0 to nb-1 ! for j=0 to nb-1 ! .w(i,j)=1 ! next j ! next i
draw_field(nb)
nm=10 ! if nb>9 then nm+=3*(nb-9) ! if nb>15 then nm+=4*(nb-15)
for k=1 to nm
  do ! i=rnd(nb) ! j=rnd(nb) ! until .m(i,j)=0
  .m(i,j)=-1 ! .w(i,j)=0
  next k
for p=0 to nb-1 ! for q=0 to nb-1
  if .m(p,q)=-1 then continue else sum=0
  for i=max(0,p-1) to min(nb-1,p+1)
    for j=max(0,q-1) to min(nb-1,q+1)
      if (p<>i or q<>j) and .m(i,j)=-1 then sum+=1
      next j
    next i
  .m(p,q)=sum
  next q ! next p
end def

def select_board_size()
button "s9" text "9 x 9" at 30,60 size 120,30
button "s15" text "15 x15" at 230,60 size 120,30
button "s21" text "21 x 21" at 430,60 size 120,30
draw text "s e l e c t   b o a r d   s i z e" at 100,20
do slowdown
  if bp("reset") then
    .t9=0 ! .w9=0 ! .t15=0 ! .w15=0 ! .t21=0 ! .w21=0
    save_scores() ! show_scores()
    end if
  if bp("s9")  then ! nb=9  ! .t9+=1  ! break ! end if
  if bp("s15") then ! nb=15 ! .t15+=1 ! break ! end if
  if bp("s21") then ! nb=21 ! .t21+=1 ! break ! end if
  until forever
fill rect 100,20 to 500,50
button "s9" delete ! button "s15" delete ! button "s21" delete
return nb
end def

def draw_field(nb)
nb2=floor(nb/2) ! d=2
bw=floor((.sw-(nb+1)*d-26)/nb)
xo=.xc-(nb2+.5)*bw-(nb2)*d ! yo=80
set buttons font name "Papyrus"
set buttons font size 400/.nb
for i=0 to nb-1 ! for j=0 to nb-1
  x=xo+j*(bw+d) ! y=yo+i*(bw+d)
  button i&"-"&j text "" at x,y size bw,bw
  next j ! next i

draw rect xo-5,yo-53-d to x+bw+d+3,y+bw+d+3
draw line xo-5,yo-d-3 to x+bw+d+3,yo-d-3
big("Minesweeper (Microsoft idea)")
end def

def check_win()
for i=0 to .nb-1 ! for j=0 to .nb-1
  if .w(i,j) then return 0
  next j ! next i
for i=0 to .nb-1 ! for j=0 to .nb-1
  if .m(i,j)=-1 then button i&"-"&j text chr$(9899)
  next j ! next i
if .nb=9 then .w9+=1 ! if .nb=15 then .w15+=1  ! if .nb=21 then .w21+=1
save_scores() ! show_scores()
big("You win !")
return 1
end def

def lose()
for i=0 to .nb-1 ! for j=0 to .nb-1
  if .m(i,j)=-1 then button i&"-"&j text chr$(9899)
  next j ! next i
save_scores() ! show_scores()
big("You lose !")
end def

def cleanup(p,q)
dim vis(.nb,.nb),sti(.nb*.nb), stj(.nb*.nb) ! sp=0
do
  vis(p,q)=1 ! button p&"-"&q delete
  for i=max(0,p-1) to min(.nb-1,p+1)
    for j=max(0,q-1) to min(.nb-1,q+1)
      if vis(i,j) then continue
      if .m(i,j)>0 then ! button i&"-"&j text .m(i,j)
        else
        button i&"-"&j delete
        sp+=1 ! sti(sp)=i ! stj(sp)=j
        end if
      vis(i,j)=1 ! .w(i,j)=0 ! pause .2
      next j
    next i
  p=sti(sp) ! q=stj(sp) ! sp-=1
  until sp=-1
end def

def show_scores()
file .f$ reset ! set buttons font size 20
file .f$ input .t9,.w9,.t15,.w15,.t21,.w21
fill rect 12,840 to 757,960
draw rect 12,840 to 757,960
draw text "9 x 9     15 x 15     21 x 21" at 200,850
draw text "Board size"   at 30,850
draw text "Games played" at 30,880
draw text "Games won"    at 30,900
draw text "Win %"        at 30,920
t$=.t9&"          "&.t15&"           "&.t21
draw text t$ at 226,880
t$=.w9&"          "&.w15&"           "&.w21
draw text t$ at 226,900
if .t9 then
  p=int(100*.w9/.t9) ! draw text p&"%" at 215,920
  end if
if .t15 then
  p=int(100*.w15/.t15) ! draw text p&"%" at 346,920
  end if
if .t21 then
  p=int(100*.w21/.t21) ! draw text p&"%" at 490,920
  end if
  button "reset" text "Reset to zero" at 600,920 size 140,30
end def

def save_scores()
if file_exists(.f$) then file .f$ delete
file .f$ print .t9,.w9,.t15,.w15,.t21,.w21
end def

def big(a$)
fill rect 20,30 to 700,70
draw font size 30 ! tw=text_width(a$)/2
draw text a$ at 400-tw,35 ! draw font size 20
end def

def bp(a$) = button_pressed(a$)

Re: Minesweeper, final version 29-02-2019

Posted: Sat Mar 30, 2019 11:16 am
by Henko
"Minesweeper" is an old game, that always came standard with the Windows operating systems. Only recently i learned that there exists a Minesweeper "world" on internet (see http://minesweeper.info/).
I made a version of it on the iPad in order to play it right away with the iPad in my hands. The iPad version is somewhat simpler than the official version, but not less difficult. As a lot of iPad owners may not have a Windows PC, i will give a short explanation of this version of the game.

The game is played on a square board, divided in a odd number of little squares. The player is given a choice out of 3 boards (9x9, 15x15, and 21x21).
After selection of the size, a number of mines are placed at random on the board. Then the program inspects each square to count how many mines are neighbouring that square. This number is placed in the inspected square. However, all squares are covered with initially blanc buttons.
Hence, for each button (square) there are three possibilities:
- the button hides a mine
- The button hides a number,
- The button hides a zero (the square has no mines as neighbour)

By touching the buttons, the player unveils the content of the square under it.
The purpose of the game is to discover the positions of the mines wthout touching a button which hides a mine. In a number of cases this can be done entirely by reasoning, in other cases, one or more "blind" choices must be made.

Some examples of reasoning in this game:
- if you hit a square with number zero, then this square has no mines as neighbours. We call this a "white" square. The number zero is not shown, but the button is simply deleted. It is important to realize that you can hit all buttons directly around a white square without the possibility of hitting a mine. If one or more of these neighbours are white squares again, the same procedure can be repeated for each of these white squares and so on, and so on.... a white area appears, surrounded by visible fields with numbers 1 or higher. As this does not require any reasoning, the program does it for you (as does the original M'soft game). So, it is possible that hitting a button may start a running process of unveiling a white area. As a matter of fact, this is a necessary condition to be able to start the reasoning proces.
- If the board contains a square marked "1" and the square has only one hidden neighbour, then obviously that neighbour must be a mine. From that moment you can regard that square as unveiled, although it remains covered by a button.
- If the board contains a square marked "2" and the square has only 2 hidden neighbours left, then you know that both these neighbours must be mines. The same reasoning is valid for squares with higher marks.

The game ends if all squares, not containing a mine, have been revealed. This is a win.
If you hit a mine, that is a loosed game.
The scoreboard displayes the number of games played with each board size, and the number of wins and as percentage of the total.

Upon starting a game, you depend on pure luck for not hitting a mine. At least one white area is needed to be able to start reasoning. For that reason, i will extend the present version (which will no longer be the "final" version) with a facility that a game is not marked as a loosed game if a mine is hit before the first white square (and area) is unveiled (the original Minesweeper shows no mercy at that point).
I will also let the program check each round if mines exist with all squares around it unveiled. In that case the mine will be shown.

In the original game, the time needed to produce a winning game is a record item for the Minesweeper "community". Perhaps i will add that also to the upcoming "final" version 😁, and keep track of the shortest time for each board size in the scoreboard.

Re: Minesweeper, final version 29-02-2019

Posted: Sun Mar 31, 2019 3:14 pm
by Henko
A new "final" version (31-02-2019)

Added:
- loosed games are not counted as such if a mine is hit before a white square was touched
- detected mines are shown
- the use time in seconds is shown in the score window

Code: Select all

' minesweeper, version 31-03-2019 (Microsoft game)
'
init_prog()

do slowdown
  for i=0 to nb-1
    for j=0 to nb-1
      b$=i&"-"&j
      if bp(b$) then
        w(i,j)=0 ! res=m(i,j)
        if res=-1 then ! beep ! lose() ! stop ! end if
        if res>0 then button b$ text res else cleanup(i,j)
        if check_win() then ! beep ! stop ! end if
        check_mines()
        end if
      next j
    next i
  until forever
end

def init_prog()
graphics ! graphics clear .8,.8,.8
draw color 0,0,0 ! draw size 3 ! fill color .8,.8,.8
get screen size .sw,.sh ! .xc=.sw/2
.f$="cg#76zk"
if file_exists(.f$) then show_scores()
nb=select_board_size() ! .nb=nb
dim .m(nb,nb),.w(nb,nb)
for i=0 to nb-1 ! for j=0 to nb-1 ! .w(i,j)=1 ! next j ! next i
draw_field(nb) ! .no_loose=1
nm=10 ! if nb>9 then nm+=3*(nb-9) ! if nb>15 then nm+=4*(nb-15)
for k=1 to nm
  do ! i=rnd(nb) ! j=rnd(nb) ! until .m(i,j)=0
  .m(i,j)=-1 ! .w(i,j)=0
  next k
for p=0 to nb-1 ! for q=0 to nb-1
  if .m(p,q)=-1 then continue else sum=0
  for i=max(0,p-1) to min(nb-1,p+1)
    for j=max(0,q-1) to min(nb-1,q+1)
      if (p<>i or q<>j) and .m(i,j)=-1 then sum+=1
      next j
    next i
  .m(p,q)=sum
  next q ! next p
time reset
end def

def select_board_size()
button "s9" text "9 x 9" at 30,60 size 120,30
button "s15" text "15 x15" at 230,60 size 120,30
button "s21" text "21 x 21" at 430,60 size 120,30
draw text "s e l e c t   b o a r d   s i z e" at 100,20
do slowdown
  if bp("reset") then
   .t9=0 ! .w9=0 ! .t15=0 ! .w15=0 ! .t21=0 ! .w21=0
    save_scores() ! show_scores()
    end if
  if bp("s9")  then ! nb=9  ! .t9+=1  ! break ! end if
  if bp("s15") then ! nb=15 ! .t15+=1 ! break ! end if
  if bp("s21") then ! nb=21 ! .t21+=1 ! break ! end if
  until forever
fill rect 100,20 to 500,50
button "s9" delete ! button "s15" delete ! button "s21" delete
return nb
end def

def draw_field(nb)
nb2=floor(nb/2) ! d=2
bw=floor((.sw-(nb+1)*d-26)/nb)
xo=.xc-(nb2+.5)*bw-(nb2)*d ! yo=80
set buttons font name "Papyrus"
set buttons font size 400/.nb
for i=0 to nb-1 ! for j=0 to nb-1
  x=xo+j*(bw+d) ! y=yo+i*(bw+d)
  button i&"-"&j text "" at x,y size bw,bw
  next j ! next i

draw rect xo-5,yo-53-d to x+bw+d+3,y+bw+d+3
draw line xo-5,yo-d-3 to x+bw+d+3,yo-d-3
big("Minesweeper (Microsoft idea)")
end def

def check_win()
for i=0 to .nb-1 ! for j=0 to .nb-1
  if .w(i,j) then return 0
  next j ! next i
for i=0 to .nb-1 ! for j=0 to .nb-1
  if .m(i,j)=-1 then button i&"-"&j text chr$(9899)
  next j ! next i
.tim=time()
if .nb=9 then .w9+=1 ! if .nb=15 then .w15+=1  ! if .nb=21 then .w21+=1
save_scores() ! show_scores()
big("You win !")
return 1
end def

def lose()
for i=0 to .nb-1 ! for j=0 to .nb-1
  if .m(i,j)=-1 then button i&"-"&j text chr$(9899)
  next j ! next i
if .no_loose=0 then ! save_scores() ! show_scores() ! end if
big("You lose !")
end def

def check_mines()
for p=0 to .nb-1 ! for q=0 to .nb-1
  if .m(p,q)=-1 then m=1 else continue
  for i=max(0,p-1) to min(.nb-1,p+1) 
    for j=max(0,q-1) to min(.nb-1,q+1)
      if i=p and j=q then continue
      if .w(i,j) then ! m=0 ! break ! end if
      next j
    if m=0 then break
    next i
  if m then button p&"-"&q text chr$(9899)
  next q ! next p
end def

def cleanup(p,q)
dim vis(.nb,.nb),sti(.nb*.nb), stj(.nb*.nb)
.no_loose=0 ! sp=0
do
  vis(p,q)=1 ! button p&"-"&q delete
  for i=max(0,p-1) to min(.nb-1,p+1)
    for j=max(0,q-1) to min(.nb-1,q+1)
      if vis(i,j) then continue
      if .m(i,j)>0 then ! button i&"-"&j text .m(i,j)
        else
        button i&"-"&j delete
        sp+=1 ! sti(sp)=i ! stj(sp)=j
        end if
      vis(i,j)=1 ! .w(i,j)=0 ! pause .2
      next j
    next i
  p=sti(sp) ! q=stj(sp) ! sp-=1
  until sp=-1
end def

def show_scores()
file .f$ reset ! set buttons font size 20
file .f$ input .t9,.w9,.t15,.w15,.t21,.w21
fill rect 12,840 to 757,960
draw rect 12,840 to 757,960
draw text "9 x 9     15 x 15     21 x 21" at 200,850
draw text "Board size"   at 30,850
draw text "Games played" at 30,880
draw text "Games won"    at 30,900
draw text "Win %"        at 30,920
t$=.t9&"          "&.t15&"           "&.t21
draw text t$ at 226,880
t$=.w9&"          "&.w15&"           "&.w21
draw text t$ at 226,900
if .t9 then
  p=int(100*.w9/.t9) ! draw text p&"%" at 215,920
  end if
if .t15 then
  p=int(100*.w15/.t15) ! draw text p&"%" at 346,920
  end if
if .t21 then
  p=int(100*.w21/.t21) ! draw text p&"%" at 490,920
  end if
  button "reset" text "Reset to zero" at 600,920 size 140,30
if .tim then draw text "time= "&int(.tim)&" sec." at 580,870
end def

def save_scores()
if file_exists(.f$) then file .f$ delete
file .f$ print .t9,.w9,.t15,.w15,.t21,.w21
end def

def big(a$)
fill rect 20,30 to 700,70
draw font size 30 ! tw=text_width(a$)/2
draw text a$ at 400-tw,35 ! draw font size 20
end def

def bp(a$) = button_pressed(a$)

Re: Minesweeper, final version 29-02-2019

Posted: Mon Apr 01, 2019 8:28 am
by Dutchman
Thank you Henko for the detailed explanation. As a mac user I didn't know the game.
I will definitely try it now, hoping it won't become an addiction. :D
Good plan for ongoing upgrading. 8-)