Programming Gambas from Zip/Ascii
Game of Concentration
[edit | edit source]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.
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.
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).
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 | edit source]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).
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]