Programming Gambas from Zip/Ascii

Game of Concentration

edit

You know the game: cards are arranged face down, you turn over a card, then you try to remember where you have seen its match. It’s somewhere...thinks...yes, it was here! You turn it over to find … no match! It was somewhere else. You turn the cards over and your friend takes a turn. If you do turn over matching cards, you take the cards. Whoever has the more cards at the end wins.

 
Concentration, or "Memory", played with cards

In this version, there is only one player and you are racing against the clock to match all the cards.

The form is 508 wide and 408 high, but that is what I needed when I used Cooper Black for the font and +12 for the increased font size. Cooper Black has nice big black letters.

The File menu has three items, Give Up, New Game and Quit.

The form has its arrangement set to Fill so that the solitary gridview gv1 fills the whole window. It shouldn’t be resized, though, so set resizeable to False.

What we will do is have a 6x6 grid. All squares will be pale yellow. In memory is a kind of mirror image of the grid in an array called z . Arrays we have used up to now have been lists. A list is a series of items in a line. Lines are one-dimensional. The one dimension is their length. Grids are 2-dimensional and their two dimensions are length and width. For gridviews there are rows and columns. In our case z has rows and columns just as the gridview gv1 has. Public z As New String[6, 6] will create z as this in-memory grid. The top left cell of the gridview is gv1[0,0]. The top left corner of z is z[0,0]. The bottom right cell of the gridview is gv1[5,5], and the bottom right corner of z is z[5,5]. 36 cells in the gridview, like the 36 memories in z, are arranged in rows and columns.

The gridview shows the “cards” as we turn them over. The array z has the “underneath sides of the cards”. Pictures of your favourite relatives would be nice, but for now they will have big, Cooper Black letters, one on each.

You click a grid cell. The card turns over: we show the letter that we have hidden in z. If we kept doing this every time we clicked a grid cell there would be no game. We would just gradually reveal all the letters. So when you show a letter we raise a flag that says “One letter is showing”. If that flag is up, the next time you click a cell we shall know that a second card has been turned over and it is time to check for a match. No match? Hide the letters and lower the flag—one letter is NOT showing. If we have a match perhaps it is the very last card and you have finished the game. Or perhaps it is not the very last card, and you can leave the cards turned over (their letters showing) and play on, remembering to lower that flag because the next click will again be clicking the first-card-of-the-pair. The flag is a boolean (true/false, hat on/hat off) variable called OneShowing.

How do we know the game is finished? Every time we have a match, add 2 to a running total of how many cards are out of the game. When that total reaches 36, all cards have been matched. The variable that keeps count is called TurnedOver and it is an integer. It is a public (or private, as in “private to the form”—it doesn’t matter) variable, declared right at the start with all the other variables that need to exist for the duration of the form and not disappear when a sub finishes.

There is a timer included. The built-in function Timer() represents the number of seconds since the application started. As soon as you make your first click the time is stored in StartTime. How long you took to play is put into the variable secs.

Dim secs As Integer = Timer() - StartTime

The game board is set up in the Initialise sub. It zeroes the things that have to be zeroed. It calls on GetRandomLetters to make a list called s[] of the letters that will be distributed to the cells of z with its 6 rows and 6 columns. s needs to have 18 random letters, each repeated so there are matching pairs. It has 36 items altogether. Here are the screenshots and the code.

 
Looking for Matches...
 
“Give Up” shows where they all were.
 
The 508 x 458 form. Arrangement property = Full. gv1 has expand set to True.
Const nRows As Integer = 6
Const nCols As Integer = 6
Const PaleYellow As Integer = &hFFFFAA
Public TurnedOver As Integer
Public z As New String[nRows, nCols]
Public s As New String[]
Public OneShowing As Boolean
Public FirstRow As Integer
Public FirstColumn As Integer
Public StartTime As Float

Public Sub Form_Open()

  Dim i, j As Integer
  gv1.Columns.count = nCols
  gv1.Rows.Count = nRows
  gv1.Background = Color.DarkBlue
  For i = 0 To gv1.Columns.max
    gv1.Columns[i].Alignment = Align.Center
    gv1.Columns[i].Width = 84
    For j = 0 To gv1.Rows.max
      gv1[i, j].Padding = 16
    Next
  Next
  Initialise

End

Public Sub Initialise()

  Dim i, j, n, nCol, nRow As Integer
  Dim c As String

  nCol = gv1.Columns.max
  nRow = gv1.Rows.Max
  GetRandomLetters(18) 'each letter twice
  For i = 0 To nRow
    For j = 0 To nCol
      c = s[n]
      z[i, j] = c
      gv1[i, j].ForeGround = Color.Black
      gv1[i, j].Text = ""
      gv1[i, j].Background = Color.DarkBlue
      n = n + 1
    Next
  Next
  TurnedOver = 0

End

Public Sub GetRandomLetters(Count As Integer)

  Dim i, p, r1, r2 As Integer
  Randomize 'different random numbers every time
  s.clear
  p = Rand(Asc("A"), Asc("Z")) 'start with any letter
  Do Until s.count >= 2 * Count
    s.Add(Chr(p))
    s.Add(Chr(p)) 'other one in the pair
    p += 1
    If p > Asc("Z") Then p = Asc("A") 'back to the start
  Loop
  For i = 0 To s.Count 'c.shuffle() 'When I update to 3.13 I can use this!
    r1 = Rand(0, s.max)
    r2 = Rand(0, s.max)
    Swap s[r1], s[r2]
  Next

End

Public Sub MenuNew_Click()
  Initialise
End

Public Sub MenuQuit_Click()
  Quit
End

Public Sub gv1_Click()

  If TurnedOver = 0 Then StartTime = Timer 'begin timing from the first click
  If OneShowing Then
    gv1[gv1.row, gv1.Column].Background = Color.DarkBlue
    gv1[gv1.row, gv1.Column].Foreground = Color.White
    gv1[gv1.row, gv1.Column].Text = z[gv1.row, gv1.Column]
    gv1.Refresh
    Wait 'finish pending operations and do the refresh
    Evaluate(gv1.row, gv1.Column)
  Else
    FirstRow = gv1.row
    FirstColumn = gv1.Column
    gv1[FirstRow, Firstcolumn].Background = Color.DarkBlue
    gv1[FirstRow, Firstcolumn].Foreground = Color.White
    gv1[FirstRow, Firstcolumn].Text = z[FirstRow, FirstColumn]
    OneShowing = True
  Endif

End

Public Sub Evaluate(row As Integer, column As Integer)

  If z[FirstRow, FirstColumn] = gv1[row, column].Text Then 'a match
    TurnedOver += 2
    If TurnedOver = nRows * nCols Then
      Dim t As String
      t = TheTime()
      Message("Well done!<br>You took " & t)
      Initialise
    Else
      Wait 0.5
      gv1[FirstRow, FirstColumn].Text = ""
      gv1[row, column].Text = ""
      gv1[FirstRow, FirstColumn].Background = PaleYellow
      gv1[row, column].Background = PaleYellow
    Endif

  Else 'no match
    Wait 1 'second
    gv1[FirstRow, FirstColumn].Text = ""
    gv1[row, column].Text = ""
    gv1[FirstRow, FirstColumn].Background = Color.DarkBlue
    gv1[row, column].Background = Color.DarkBlue

  Endif
  OneShowing = False

End

Public Sub TheTime() As String

  Dim secs As Integer = Timer() - StartTime
  Dim h As Integer = secs / 60 / 60

  Secs -= h * 60 * 60
  Dim m As Integer = secs / 60
  Secs -= m * 60
  Return If(h > 0, Str(h) & "h ", "") & If(m > 0, Str(m) & "m ", "") & Str(secs) & "s"

End

Public Sub MenuGiveUp_Click()

  For i As Integer = 0 To nRows - 1
    For j As Integer = 0 To nCols - 1
      If gv1[i, j].Text = "" Then
        gv1[i, j].ForeGround = Color.Red
        gv1[i, j].Text = z[i, j]
        gv1[i, j].Background = Color.DarkRed
      End If
    Next
  Next

End

If you prefer to work with clicking pictures, you will need a folder called Pix located in your Pictures folder. You need to put 18 pictures in it (jpg or png).

 
Now, where is the other Snoopy?
 
Give up” shows where they all were.

In the code that follows, the picture files are read into an array of images. Images and Pictures in Gambas differ in what part of memory they are stored in. Images, unlike pictures, can be stretched to fit in an area with any given width and height. So the images in the array of eighteen are, when one is needed, put into a single Image called Img, stretched to fit one of the grid cells. The resulting image, now the right size, is converted to a picture using the Picture method that images have.

To show an image the cell’s Picture property is set to the converted image. To hide it the picture property is set to Null. This happens when you click on a cell.

There is a corresponding two-dimensional (rows/columns) array of picture names. To see if there is a match, the names of the pictures in the two clicked-on cells are compared.

It is time to congratulate the winner when 18 cells have been correctly matched (two at a time).

Const nRows As Integer = 6
Const nCols As Integer = 6
Const PaleYellow As Integer = &hFFFFAA
Public TurnedOver As Integer
Public z As New String[nRows, nCols] 'names of the pictures
Public Images As New Image[nRows, nCols] 'the images themselves
Public Img As Image
Public s As New String[]
Public OneShowing As Boolean
Public FirstRow As Integer
Public FirstColumn As Integer
Public StartTime As Float

Public Sub Form_Open()

  Dim i, j As Integer

  gv1.Columns.count = nCols
  gv1.Rows.Count = nRows
  gv1.Background = PaleYellow
  For i = 0 To gv1.Columns.max
    gv1.Columns[i].Alignment = Align.Center
    gv1.Columns[i].Width = 84
  Next
  Initialise

End

Public Sub Initialise()

  Dim i, j, n, nCol, nRow As Integer
  Dim c As String

  nCol = gv1.Columns.max
  nRow = gv1.Rows.Max
  GetRandomLetters 'each picture twice
  For i = 0 To nRow
    gv1.Rows[i].Height = 70
    For j = 0 To nCol
      c = s[n]
      z[i, j] = c
      gv1[i, j].Picture = Null
      gv1[i, j].Background = Color.DarkCyan
      n = n + 1
    Next
  Next
  TurnedOver = 0

End

Public Sub GetRandomLetters()

  Dim i, j, n As Integer
  Dim path As String = User.Home &/ "Pictures/Pix/" 'must be 18 pictures in here
  Dim cellW As Float = gv1[0, 0].Width
  Dim cellH As Float = gv1[0, 0].Height
  Dim scale As Float = Min(CellW, CellH)

  If Not Exist(Path) Then
    Message("Please create a folder called Pix in your Pictures folder.<br>Put 18 pictures in it.")
    Quit
  Endif
  s = Dir(path, "*.png")
  s.Insert(Dir(path, "*.jpg"))
  If s.Count < 18 Then
    Message("Please put 18 pictures in the Pix folder inside your Pictures folder.<br>There were only " & s.Count)
    Quit
  Endif
  s.Insert(s) 'second copy
  s.Shuffle
  For i = 0 To gv1.Rows.Max
    For j = 0 To gv1.Columns.Max
      Images[i, j] = Image.Load(path & s[n])
      n += 1
    Next
  Next

End

Public Sub MenuNew_Click()

  Initialise

End

Public Sub MenuQuit_Click()

  Quit

End

Public Sub gv1_Click()

  If TurnedOver = 0 Then StartTime = Timer 'begin timing from the first click
  If OneShowing Then
    gv1[gv1.row, gv1.Column].Background = Color.White
    Img = Images[gv1.row, gv1.Column].stretch(70, 70)
    gv1[gv1.row, gv1.Column].Picture = Img.Picture
    gv1.Refresh
    Wait 'finish pending operations and do the refresh
    Evaluate(gv1.row, gv1.Column)
  Else
    FirstRow = gv1.row
    FirstColumn = gv1.Column
    gv1[FirstRow, Firstcolumn].Background = Color.White
    Img = Images[FirstRow, FirstColumn].stretch(70, 70)
    gv1[FirstRow, Firstcolumn].Picture = Img.Picture
    OneShowing = True
  Endif

End

Public Sub Evaluate(row As Integer, column As Integer)

  If z[FirstRow, FirstColumn] = z[row, column] Then 'a match
    TurnedOver += 2
    If TurnedOver = nRows * nCols Then
      Dim t As String
      t = TheTime()
      Message("Well done!<br>You took " & t)
      Initialise
    Else
      Wait 0.5 'half second
      gv1[FirstRow, FirstColumn].Picture = Null
      gv1[row, column].Picture = Null
      gv1[FirstRow, FirstColumn].Background = PaleYellow
      gv1[row, column].Background = PaleYellow
    Endif

  Else 'no match
    Wait 1 'second
    gv1[FirstRow, FirstColumn].Picture = Null
    gv1[row, column].Picture = Null
    gv1[FirstRow, FirstColumn].Background = Color.DarkCyan
    gv1[row, column].Background = Color.DarkCyan

  Endif
  OneShowing = False

End

Public Sub TheTime() As String

  Dim secs As Integer = Timer() - StartTime
  Dim h As Integer = secs / 60 / 60

  Secs -= h * 60 * 60
  Dim m As Integer = secs / 60
  Secs -= m * 60
  Return If(h > 0, Str(h) & "h ", "") & If(m > 0, Str(m) & "m ", "") & Str(secs) & "s"

End

Public Sub MenuGiveUp_Click()

  For i As Integer = 0 To nRows - 1
    For j As Integer = 0 To nCols - 1
      If gv1[i, j].Picture = Null Then
        Img = Images[i, j].Stretch(70, 70)
        gv1[i, j].Picture = Img.Picture
        gv1[i, j].Background = Color.White
      End If
    Next
  Next

End

ASCII Codes

edit

Characters (letters, digits, punctuation symbols) are stored in a computer’s memory by numbers. The most widely used system is ASCII, American Standard Code for Information Interchange. It was developed in the United States, and Wikipedia tells me the governing body prefers to call it US-ASCII because it uses the American dollar sign ($) and the Latin alphabet. Whenever you hit a key on the keyboard one of those code numbers goes into the computer. Even the non-printing characters have ASCII codes. The spacebar is 32. Hit the Delete key and 127 would go in. The Backspace key sent the number 8. To confuse you, the ASCII code for the digit ‘1’ is 49. What the computer does with these code numbers is up to the application. And you are writing the applications. In the above program, type what you like and nothing happens at all (except for CTRL-G which I have for “Give Up”, CTRL-N which is “New Game” and CTRL-Q, which is the shortcut for Quit).

 
TypeWriter

On the old manual typewriters at the end of a line you had to flick a lever and the roller with the paper going around it would zip back to the start of the line (Return) and pull the paper up a line (Linefeed) ready to start typing the next line. Return is 13. ASCII 13 is also Control-M (written ^M) and in programming languages is sometimes written \r. Linefeed is 10 and is also Control-J (^J). There is a Formfeed control, Control-L, that used to go to a new page (ASCII 12). You wouldn’t remember manual typewriters unless you spend time in museums, but for me it is like yesterday (sigh). Nowadays ASCII is largely replaced by Unicode. ASCII was limited to 128 characters. Unicode can display 137,993 characters, says Wikipedia—enough for all sorts of non-English characters and all the emojis you could ever want.

Even the original ASCII gave problems for people who spoke languages other than English. Wikipedia has the amusing example of ‘a Swedish programmer mailing another programmer asking if they should go for lunch, could get "N{ jag har sm|rg}sar" as the answer, which should be "Nä jag har smörgåsar" meaning "No I've got sandwiches" ’ and he or she would just have to put up with it.[1]

Programming Gambas from Zip
 ← ContextualMenus Ascii RadioButtons → 
  1. https://en.wikipedia.org/wiki/ASCII