If you haven't already, head over to https://codingwithdrew.com/category/golang/ and follow some of the prerequisite articles on how to use GoLang and set up your environment.
Overview:
We will create a package that will simulate playing cards you'd use for poker or the like.
Through the course of this project, we are going to create a number of functions that will simulate how we interact with the deck of cards. Eventually we will have the following functions:
newDeck
-- create a list of playing cards. Essentially an array of strings where each element of an array will represent a single face of card.We haven't discussed an array just yet, but keep that question in your pocket, we will get there ๐
deal
-- create a 'hand' of cards.shuffle
-- shuffles all the cards in the decknewDeckFromFile
-- load a list of cards from the local machine.saveToFile
-- save a list of cards to a file on the local machineprint
-- log out of the contents of a deck of cards
How to organize the project
Currently our project files will be structured like this:
~/Dev/Cards/ Directory
|-- main.go
main.go
- In your terminal run
cd
into your development directory. _i.e. ~/dev/๐ก
cd
is used to "change directories. You can use it to navigate from one folder to another.cd ..
for example would change directories up 1 level. andcd ~/
would change directories to your~/
also known as your "root" directory.
- Run
touch main.go
๐ก
touch
can be used to create a new file, it will also update the creation time stamp on a file if used more than once.
- Run
code .
which if you remember from our development setup will create a new VisualStudio Code workspace for you to work in.
Remember we need some boilerplate for this main.go file:package main func main(){ }
- Press
command
+option
+S
to save your working directory and all files that changed. Let's take a moment to discuss some Go syntax๐ก Go is what we consider a Statically typed language as opposed to a Dynamically typed language like Javascript. This means when we declare variables, we also have to declare what kind of value will be declared. Dynamically typed languages do not care what kind of value that you assign to their variables, this is not a better or worse characteristic. There are 3 other basic types of variables in go.
๐ก There are 4 types of variables you can declare in go:
1. bool -- can be true or false
2. string -- can be text of any sort, including numbers but if you use numbers they will not be integers for math.
3. int -- can be any whole number value you can type into a computer.
4. float64 - can be a number with a decimal after it.
๐ก This is one way to define a variable inside of go:
๐ก Here is another way to define the same variable above that can make Go feel a bit more dynamically:
๐ก If you want to change the value of a variable, just use
=
instead of:=
.
- Let's create a new variable or
var
and assign it astring
that represents a playingcard
then use the"fmt"
package to print it out. It should look like this:package main func main(){ var card string = "This is your first card" fmt.Println(card) }
๐จ A pretty neat little thing just happened to our code, did you notice? When we saved, the Go extensions added to our VisualStudio code automatically added an import statement for
"fmt"
even though we didn't add it. This is because in our function we calledfmt.Println()
.๐ก In the code above you will see there is
fmt.Println(card)
wherecard
is an argument in a list of 1 arguments we passed to the functionfmt.Println()
. That function will call thecard
variable we declared as astring
"This is your first card" and since we do not define thefmt
function in our project, we have to import it, which is why VSCode automagically imports it when you save. Pretty neat stuff!
- Let's take what we've learned so far and apply it. Right now, when you call "card" you get "this is your first card" but what if we wanted it to be a result of a list of cards chosen at random? Wouldn't that be more realistic?
So the first thing we will need to do is create a new function. It will follow the same syntax as the main() function above. It should look like this:
package main
import "fmt"
func main(){
card := newCard()
fmt.Println(card)
}
func myCard() {
return "Ace of Diamonds"
}
๐ก Note the use of camel case. Camel case is the naming convention of combining words where the first letter of every word after the first one are capitalized. Example: learning
G
oI
sF
un().
- Press
command
+option
+S
to save your working directory and all files that changed.
๐ก If we run the code as is, we will have a compiler error because it will have trouble inferring the type of variable
card
is supposed to be. This is because we are declaringcard
without saying what type it is and assigning a function to it's value. To resolve this, we have to declare it as astring
after the()
but before the{}
. Simply put, this means that "When executed, this function will return a value of type string".๐ก Important note, this is only required when using the
:=
syntax.
Let's fix the issue with our code:
package main
import "fmt"
func main(){
card := newCard()
fmt.Println(card)
}
func myCard() string {
return "Ace of Diamonds"
}
- Before we really can work with this project seriously, we need more tools in our tool box. Let's look at two different but similar data structures.
- array -- fixed length list of things.
myList := []int{1,2,3,4,5}
- slice -- an array that can grow or shrink.
mySlice := []string{"Reverse", "Two of Hearts", "Jack of Spades"}
- array -- fixed length list of things.
๐ก Slices & Arrays have to be declared with a data type and every element of a slice must be of the same type so you couldn't have a
mySlice := ["string", 4, "float64", true]
So, how do we add a new element into a slice? Let's consider mySlice from above and how to add "ace of clubs to the end of the slice list
mySlice := []string{"Reverse", "Two of Hearts", "Jack of Spades"}
mySlice = append(myslice, "Ace of Clubs")
๐ก It's important to recognize that by appending mySlice, we aren't editing the original variable but instead creating a new variable and adding "Ace of Clubs" to the end. This is an important distinction.
How do we iterate over our entire slice?
Let's write some code and then break it down:
- Modify
main.go
to look like this:package main import "fmt" func main() { cards := []string{"Ace of Clubs", myCard()} cards = append(cards, "King of Hearts") for i, card := range cards { fmt.Println(i, card) } } func myCard() string { return "Ace of Diamonds" }
๐ก In the code above, we are looping through all the values of
cards
and printing their value. Let's demonstrate!๐จ If you forget to add
string
after declaring themyCard()
function, you will get an error that looks like this:
./main.go:15:2: too many arguments to return
have (string)
want ()
- Press
command
+option
+S
to save your working directory and all files that changed.
- We want to compile and execute our main.go file se we can see what happens when we run our code. From the terminal run
go run main.go
, the file output will look like this:% go run main.go 0 Ace of Clubs 1 Ace of Diamonds 2 King of Hearts
๐ก The numbers you see in front of each value is the
index
ori
which if you are familiar with any other coding language, arrays always start with 0 so the 3rd value in an array/slice would have an index of 2.
What is the code from step 10. really doing?
๐ก
๐ก The most important element is the
range cards
,range
is a keyword we use when we want to iterate over every single value inside of a slice.๐ก Note we are using the same
:=
syntax in the code above. What we are doing is assigning acard
to every element withincards
. Normally, you wouldn't write out:=
if you were to write this code out in long form manually because you wouldn't be declaring a new variable but in a for loop, we are throwing out the previous values for the index (i
) and thecard
.
- In terminal, let's save our work and stage our work in git.
$ git add .
๐ก
git add
followed by a file name will stage those files to a git repository. usinggit add .
will add all files in the current directory to our git staging. The workflow for git is to save all changes, stage the changes, commit the changes to local repository, push the changes to the remote repository.๐ก You can see the status of your active git branch by running
git status
or see a readout of all the latest commits usinggit log
(you can exit usingq
).
- Commit the staged changes using
git commit -m "variables, slices, and loops"
.
- You should now be able to push those changes from your local git directory to the gitlab repository using
git push origin/master
orgit push
. Since we set up ed25519 SSH connection, you'll need to provide your passcode.
Drew is a seasoned DevOps Engineer with a rich background that spans multiple industries and technologies. With foundational training as a Nuclear Engineer in the US Navy, Drew brings a meticulous approach to operational efficiency and reliability. His expertise lies in cloud migration strategies, CI/CD automation, and Kubernetes orchestration. Known for a keen focus on facts and correctness, Drew is proficient in a range of programming languages including Bash and JavaScript. His diverse experiences, from serving in the military to working in the corporate world, have equipped him with a comprehensive worldview and a knack for creative problem-solving. Drew advocates for streamlined, fact-based approaches in both code and business, making him a reliable authority in the tech industry.