Skip to content
Image with logo, providing a link to the home page
  • United Stated of America flag, representing the option for the English language.
  • Bandeira do Brasil, simbolizando a opção pelo idioma Português do Brasil.

Ideas, Rules, Simulation: Coins, Dice and Rectangles

Illustrations of images representing the possible results of throwing coins and dice, resulting from the programs created for JavaScript with HTML Canvas, GDScript with Godot Engine, Python with PyGame, and Lua with LÖVE. The image also provides a link to this website: <www.francogarcia.com>, as well as the account francogarciacom, used for the Twitter and Instagram of the author.

Image credits: Image created by the author using the program Inkscape; icons by Font Awesome.

Requirements

The material for Ideas, Rules, Simulations promotes project based learning (interactive simulations and digital games) using multimedia resources. As such, it is complimentary to the material of Learn Programming, which introduces fundamentals and basic programming techniques for programming, with examples for JavaScript, Python, Lua and GDScript (for Godot Engine).

For Ideas, Rules, Simulations you will need a configured development environment for one of the previous languages:

JavaScript (for browsers) and GDScript provide built-in support for multimedia content.

I am also considering improving the online programming editors provided in this website, to support an interactive course. Currently, the page with tools provide options for:

The editors support images in the browser. However, Python and Lua use the JavaScript syntax for the canvas. If I create an abstraction and add support for audio, they could become valid tools (at least for the first activities).

However, it is worth configuring an Integrated Development Environment (IDE), or a combination of text editor with interpreter as soon as possible, to enable you to program using your machine with greater efficiency. Online environments are practical, though local environments are potentially more complete, efficient, faster and customizable. If you want to become a professional, you will need a configured development environment. The sooner you do it, the better.

Version in Video

This entry has video versions on the author's YouTube channel:

However, this text version is more complete.

Documentation

Practice consulting the documentation:

Most links have a search field. In Lua, the documentation is in a single page. You can search for entries using the shortcut Ctrl F (search).

Heads or Tails?

The previous topic (Randomness and Noise) of Ideas, Rules, Simulation introduced the use of random numbers (technically, pseudorandom numbers) to help the creation of stochastic simulations.

Throwing coins and dice are two of the simplest examples of simulations with randomness. The required implementation can be described in a few phrases:

  1. For a coin, one can draw a random real number between 0.0 and 1.0, divide the interval by 0.5, and choose the interval 0.0 to 0.5 (non-inclusive) for heads, and 0.5 (inclusive) to 1.0 (non-inclusive) for tails. It is equally valid to invert the intervals for heads and tails.

    Another possibility would be drawing a random integer number, use the remainder of a division by 2, and match 0 as the remainder to heads and 1 for tails (or vice-versa). To make the code easier to read, a constant HEAD with value 0 could be created for heads, and a constant TAIL with value 1. If you would rather define the names using the plural terms, you could. The author considers the singular term more representative for the outcome of a single coin toss (though this is personal preference, as the terms use the singular form in Portuguese).

  2. For an honest die (that is, with equal probabilities for each possible result) with 6 faces, one can draw a random integer number, calculate the remainder of a division by 6, and add 1 to the result. The result would be a number ranging from 1 to 6.

Even simpler, the operations can be trivially performed using the random_integer() function that was defined in the previous topic. For an honest die with six faces, calling the function as random_integer(1, 6) would be sufficient. Analogously, random_integer(0, 1) could be an alternative for a coin.

As the part of the rules for the simulation is simple, the remainder of this section focuses on conditional structures, the creation and use of functions and procedures, repetition structures, and refactoring. The purpose is explaining them to people who have not followed the material from Learn Programming.

If you can already use them, you can skip to the next section to start creating graphics for the output.

Nevertheless, it is interesting that, often, it is possible to create prototypes of simulations without using graphics. In some cases, it is simpler and faster to test an idea using a program for console (terminal) than starting with graphics. Once the implementation of the prototype is satisfactory, you can start creating the graphics for a better presentation of the results.

Variables and Assignments

For a gentle introduction, the explanation will start from variables.

As the simulation is simple, a variable to store the random number is sufficient. It can be called result.

To draw a random number that varies each time the program is run, a seed must be generated. Next, the function to generate a pseudorandom number can be called to draw the number.

extends Control

func _ready():
    randomize()

    var result = randi()
    print(result)
function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let result = random_integer(0, 1)
console.log(result)
import random

random.seed()

result = random.randint(0, 1)
print(result)
math.randomseed(os.time())

local result = math.random(0, 1)
print(result)

In Python and Lua, random.randint() and math.random() allow choosing a range for the numbers. In JavaScript, the implementation of random_integer() also allows doing that. However, randi() in GDScript does not. For an example that can be applied to any programming language, the implementation could modify the range to something like 1 to 10000.

function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let result = random_integer(1, 10000)
console.log(result)
import random

random.seed()

result = random.randint(1, 10000)
print(result)
math.randomseed(os.time())

local result = math.random(1, 10000)
print(result)

This way, the generate number became large in all implementations. Thus, it will be necessary to convert it to two values to represent heads or tails.

Once again, this choice has didactic purposes. For instance, you should call math.random(0, 1) directly in Lua.

Arithmetic Operations

Computers operate on four main data types:

  1. Integer numbers;
  2. Real numbers, typically represented as floating-numbers, also called float;
  3. Logic values: True or False;
  4. Strings.

At a lower level, all types are encoded as binary numbers (refer to Learn Programming: Variables and Constants). At a lower level still, every value is a combination of presence or absence of electricity (or magnetism, or other technology used for the memory) in circuits.

In other words, there will not exist heads nor tails for the computer. There will only exist an abstraction, that will use the random number that has been drawn.

Thus, we will need to reduce the value of result to two values. This can be achieved using a arithmetic operation called division remainder (modulo). For two values, one can use modulo 2.

extends Control

func _ready():
    randomize()

    var result = randi() % 2
    print(result)
function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let result = random_integer(1, 10000) % 2
console.log(result)
import random

random.seed()

result = random.randint(1, 10000) % 2
print(result)
math.randomseed(os.time())

local result = math.random(0, 10000) % 2
print(result)

In the new version, the result will always be 0 or 1. Therefore, two values, as it is necessary for heads and tails.

Computational Thinking: Abstraction

Once gain, a computer does not know what is heads, nor tails. On the other hand, it can process an integer number.

The new goal is to associate a code that maps the values 0 and 1 to heads and tails. For the computer, result will still be 0 or 1. For someone using the program, the goal is writing heads or tails following the association that has been defined in the program.

Conditional Structures

Simulating a coin flip is a simple way to understand how to use an if/then/else structure. A Lua example can be illustrative. The emoji probably will not work in many command line interpreters; it only serves to mark the spot on which the drawing code will be written at the appropriate time.

extends Control

func _ready():
    randomize()

    var result = randi() % 2
    print(result)
    if (result == 0):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")

    print("End")
function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let result = random_integer(1, 10000) % 2
console.log(result)
if (result === 0) {
    console.log(":) 🙂")
    console.log("Heads")
} else {
    console.log("$1 👑")
    console.log("Tails")
}

console.log("End")
import random

random.seed()

result = random.randint(1, 10000) % 2
print(result)
if (result == 0):
    print(":) 🙂")
    print("Heads")
else:
    print("$1 👑")
    print("Tails")

print("End")
math.randomseed(os.time())

local result = math.random(0, 1)
print(result)

if (result == 0) then
    print(":) 🙂")
    print("Heads")
else
    print("$1 👑")
    print("Tails")
end

print("End")

In this example, print(result) (or console.log(result)) only serves the purpose of displaying the random number that was drawn. It will be removed in later examples. Likewise, print("End") (or console.log("End")) is only intended to show that the program returns to the normal flow after completing the conditional structure. It could be removed as well.

For a visualization of the code, you can refer to the following chart, created in Flowgorithm (used in Learn Programming). The flowchart provides text in Portuguese and English, for use in both translations of this topic. In the flowchart, you can notice that the conditional structure (represented by the diamond) defines two mutually exclusive execution flows. In other words, running one inhibits the execution of the other.

Representation of Lua's code using a conditional structure as a flowchart in Flowgorithm.

Another good way of visualizing how the program is running consists in using a debugger. You can learn how to use one in Learn Programming: Tests and Debugging. The video version illustrates how to use it on practice.

To explain the code, we can consider the Lua version. The variable result stores the result of drawing a random integer number. If result is equal to 0, the program runs the lines 7 and 8, and ignores the lines 10 and 11. Thus, the process runs only the part (code block) on which the condition is true (then-part). Next, the program runs line 14, and ends.

Otherwise, the program runs the lines 10 and 11, and ignores the lines 7 and 8. Thus, the process runs only the part on which the condition is false (else-part). Next, the program runs the line 14, and ends.

If one wishes, she/he may use the random number directly as part of the condition (instead of storing it on the result variable).

extends Control

func _ready():
    randomize()

    if (randi() % 2 == 0):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")

    print("End")
function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

if (random_integer(1, 10000) % 2 === 0) {
    console.log(":) 🙂")
    console.log("Heads")
} else {
    console.log("$1 👑")
    console.log("Tails")
}

console.log("End")
import random

random.seed()

if (random.randint(1, 10000) % 2 == 0):
    print(":) 🙂")
    print("Heads")
else:
    print("$1 👑")
    print("Tails")

print("End")
math.randomseed(os.time())

if (math.random(0, 1) == 0) then
    print(":) 🙂")
    print("Heads")
else
    print("$1 👑")
    print("Tails")
end

print("End")

The new version is equivalent to the previous one. It can be used when an intermediate (or temporary) result will not be further required as part of the implementation.

However, if one needs to store the value or use it many times (in other words, two or more times), the variable will be required. In Lua with LÖVE, this will be necessary to preserve the value of the random number. To the current version using the console (terminal), this is not necessary. On the other hand, if one wishes to write the result as it has been done previously, the use of the variable would be required. After all, a new call to the function that generates pseudorandom numbers could generate a different value each time.

If there existed more than two possible results of interest, one nest several conditional structures to handle each case. This can be achieved by combining a elseif in Lua, elif in Python and GDScript, and else if in JavaScript (JavaScript does not have a specialized version, hence it uses a new conventional structure). Later, this kind of construction will be used to draw faces of a die. The next subsection explains this use.

Handling More than Two Cases

In the case of a real coin, the results only can be heads or tails. On the other hand, in a simulation, you could imagine other scenarios.

After all, it is your simulation. You are the magician. In other words, you are the one who makes the rules of the simulation. Your ideas, your rules, your simulations.

For instance, perhaps in 20% of the cases the window could float in the air. In this new scenario, the chance of heads occurring would become 40%, and so would that change for tails. The simulation could draw numbers from 1 to 100 (or 0 to 99, using a remainder of a division by 100) to generate the percentages. In this case in particular, it would also be possible to use values between 1 and 10 (or 0 to 9, if using the remainder of a division by 10). Or even by 5.

Next, conditions could be defined using relational operations and comparisons.

extends Control

func _ready():
    randomize()

    var result = randi() % 10
    print(result)
    if (result < 4):
        print(":) 🙂")
        print("Heads")
    else:
        if (result < 8):
            print("$1 👑")
            print("Tails")
        else:
            print("The coin is floating on the air...")

    print("End")
function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let result = random_integer(1, 10000) % 10
console.log(result)
if (result < 4) {
    console.log(":) 🙂")
    console.log("Heads")
} else {
    if (result < 8) {
        console.log("$1 👑")
        console.log("Tails")
    } else {
         console.log("The coin is floating on the air...")
    }
}

console.log("End")
import random

random.seed()

result = random.randint(1, 10000) % 10
print(result)
if (result < 4):
    print(":) 🙂")
    print("Heads")
else:
    if (result < 8):
        print("$1 👑")
        print("Tails")
    else:
        print("The coin is floating on the air...")

print("End")
math.randomseed(os.time())

local result = math.random(1, 10)
print(result)

if (result < 4) then
    print(":) 🙂")
    print("Heads")
else
    if (result < 8) then
        print("$1 👑")
        print("Tails")
    else
        print("The coin is floating on the air...")
    end
end

print("End")

It is important noticing that, in this new version, the code requires the use of a variable to store the random value, as it is used in two comparisons. As the cases are mutually exclusive (due to the use of else), the second nested if has a condition of (result >= 4) and (result < 8), because the program already knows that the number is not less than 4. Likewise, the last else has the condition (result >= 8), for the value cannot be less than 8 at this point. The and is a logic operator.

Thus, the first verification handles the values 0, 1, 2 and 3 for heads (thus, the 40% chances for heads). The second part handles the values 4, 5, 6 and 7 for tails (the 40% for tails). Finally, the last part handles the values 8 and 9 for coin floating on air (the remaining 20%). As a remainder of division by 10 generates numbers from 0 to 9, the created conditional structure handle all cases as proposed.

As mutually exclusive cases are common, many programming languages provide a reserved word such as elif or elseif (or else if, is there is none) to make it easier to write the code.

extends Control

func _ready():
    randomize()

    var result = randi() % 10
    print(result)
    if (result < 4):
        print(":) 🙂")
        print("Heads")
    elif (result < 8):
        print("$1 👑")
        print("Tails")
    else:
        print("The coin is floating on the air...")

    print("End")
function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let result = random_integer(1, 10000) % 10
console.log(result)
if (result < 4) {
    console.log(":) 🙂")
    console.log("Heads")
} else if (result < 8) {
    console.log("$1 👑")
    console.log("Tails")
} else {
    console.log("The coin is floating on the air...")
}

console.log("End")
import random

random.seed()

result = random.randint(1, 10000) % 10
print(result)
if (result < 4):
    print(":) 🙂")
    print("Heads")
elif (result < 8):
    print("$1 👑")
    print("Tails")
else:
    print("The coin is floating on the air...")

print("End")
math.randomseed(os.time())

local result = math.random(1, 10)
print(result)

if (result < 4) then
    print(":) 🙂")
    print("Heads")
elseif (result < 8) then
    print("$1 👑")
    print("Tails")
else
    print("The coin is floating on the air...")
end

print("End")

For more information, you can refer to Learn Programming: Conditional Structures.

The new version of the implementation is equivalent to the first one. If the result is not heads, nor tails, then the coin is floating on the air. It is that simple; it is not necessary to explain the Physics. Simulation can be metaphysics, for they are originated in your imagination. In other words, from your ideas.

The remainder of this topic assumes the existence of gravity. Thus, the result will always be heads or tails.

Constants

One can define constants to make the code easier to read.

extends Control


const HEAD = 0
const TAIL = 1


func _ready():
    randomize()

    if ((randi() % 2) == HEAD):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")

    print("End")
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


if ((random_integer(1, 10000) % 2) === HEAD) {
    console.log(":) 🙂")
    console.log("Heads")
} else {
    console.log("$1 👑")
    console.log("Tails")
}

console.log("End")
import random
from typing import Final


HEAD: Final = 0
TAIL: Final = 1


random.seed()

if ((random.randint(1, 10000) % 2) == HEAD):
    print(":) 🙂")
    print("Heads")
else:
    print("$1 👑")
    print("Tails")

print("End")
local HEAD = 0
local TAIL = 1

math.randomseed(os.time())

if (math.random(0, 1) == HEAD) then
    print(":) 🙂")
    print("Heads")
else
    print("$1 👑")
    print("Tails")
end

print("End")

The next image presents a similar implementation in Flowgorithm, for visualization. As Flowgorithm does not allow defining constants, variables were used instead. In the image, the values for the constants HEAD and TAIL are reversed; as the values are symbolic, the reversion does not affect the correctness of the program.

Representation of Lua's code with a constant as a flowchart in Flowgorithm.

Since Lua 5.4, it is also possible to define a true constant using <const>.

local HEAD <const> = 0
local TAIL <const> = 1

math.randomseed(os.time())

if (math.random(0, 1) == HEAD) then
    print(":) 🙂")
    print("Heads")
else
    print("$1 👑")
    print("Tails")
end

print("End")

As ZeroBrane Studio does not include version 5.4 of the interpreter (when this topic was written), the previous code will result in a syntactic error.

Subroutines: Functions and Procedures

To make it even simpler to read the code, and write a block that can be reused several times, one can define a subroutine, such as a procedure or a function. Functions return results; procedures are used for a side effect (as, for instance, changing the memory, or input and output operations -- such as writing text or drawing on a screen).

For instance, the drawing of a random value could be implemented as a function to flip a coin called throw_coin(). You could also select other name for it (such as flip_coin(), toss_coin(), or heads_or_tails()).

extends Control


const HEAD = 0
const TAIL = 1


func throw_coin():
    return randi() % 2


func _ready():
    randomize()

    if (throw_coin() == HEAD):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")

    print("End")
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function throw_coin() {
    return random_integer(0, 1)
}


if (throw_coin() === HEAD) {
    console.log(":) 🙂")
    console.log("Heads")
} else {
    console.log("$1 👑")
    console.log("Tails")
}

console.log("End")
import random
from typing import Final


HEAD: Final = 0
TAIL: Final = 1


def throw_coin():
    return random.randint(0, 1)


random.seed()

if (throw_coin() == HEAD):
    print(":) 🙂")
    print("Heads")
else:
    print("$1 👑")
    print("Tails")

print("End")
local HEAD = 0
local TAIL = 1


function throw_coin()
    return math.random(0, 1)
end


math.randomseed(os.time())

if (throw_coin() == HEAD) then
    print(":) 🙂")
    print("Heads")
else
    print("$1 👑")
    print("Tails")
end

print("End")

The next images present a similar implementation in Flowgorithm, for visualization.

Representation of Lua's code with the definition of the function throw_coin() as a flowchart in Flowgorithm.
Representation of Lua's main program as a flowchart in Flowgorithm.

Now it is possible to call throw_coin() whenever one wishes to flip a coin.

Functions and procedures can receive parameters (arguments). To illustrate how to create and use a procedure with an argument, one could define a procedure print_coin() to write the face of the result based on a parameter storing the value of the face.

extends Control


const HEAD = 0
const TAIL = 1


func throw_coin():
    return randi() % 2


func print_coin(face):
    if (face == HEAD):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")


func _ready():
    randomize()

    print_coin(throw_coin())

    print("End")
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function throw_coin() {
    return random_integer(0, 1)
}


function print_coin(face) {
    if (face === HEAD) {
        console.log(":) 🙂")
        console.log("Heads")
    } else {
        console.log("$1 👑")
        console.log("Tails")
    }
}

print_coin(throw_coin())

console.log("End")
import random
from typing import Final


HEAD: Final = 0
TAIL: Final = 1


def throw_coin():
    return random.randint(0, 1)


def print_coin(face):
    if (face == HEAD):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")


random.seed()

print_coin(throw_coin())

print("End")
local HEAD = 0
local TAIL = 1


function throw_coin()
    return math.random(0, 1)
end


function print_coin(face)
    if (face == HEAD) then
        print(":) 🙂")
        print("Heads")
    else
        print("$1 👑")
        print("Tails")
    end
end


math.randomseed(os.time())

print_coin(throw_coin())
print("End")

The next images present a similar implementation in Flowgorithm, for visualization. The function throw_coin() has the same flowchart defined previously.

Representation of Lua's code with the definition of the procedure print_coin() as a flowchart in Flowgorithm.
Representation of Lua's main program as a flowchart in Flowgorithm.

With this change, it is simple to understand what the program does from reading print_coin(throw_coin()). Assuming that the names of the subroutines are coherent with their implementations, the program prints to the console the result of flipping a coin.

Many programming languages define a main() function as entry point (as defined in Entry Point and Program Structure) of the program. Lua does not define one (the program starts at the first line of code from the script provided to the interpreter), though we could modify the code to create one.

extends Control


const HEAD = 0
const TAIL = 1


func throw_coin():
    return randi() % 2


func print_coin(face):
    if (face == HEAD):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")


func _ready():
    randomize()

    print_coin(throw_coin())

    print("End")
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function throw_coin() {
    return random_integer(0, 1)
}


function print_coin(face) {
    if (face === HEAD) {
        console.log(":) 🙂")
        console.log("Heads")
    } else {
        console.log("$1 👑")
        console.log("Tails")
    }
}


function main() {
    print_coin(throw_coin())

    console.log("End")
}


main()
import random
from typing import Final


HEAD: Final = 0
TAIL: Final = 1


def throw_coin():
    return random.randint(0, 1)


def print_coin(face):
    if (face == HEAD):
        print(":) 🙂")
        print("Heads")
    else:
        print("$1 👑")
        print("Tails")


def main():
    random.seed()

    print_coin(throw_coin())

    print("End")


if (__name__ == "__main__"):
    main()
local HEAD = 0
local TAIL = 1


function throw_coin()
    return math.random(0, 1)
end


function print_coin(face)
    if (face == HEAD) then
        print(":) 🙂")
        print("Heads")
    else
        print("$1 👑")
        print("Tails")
    end
end


function main()
    math.randomseed(os.time())

    print_coin(throw_coin())
    print("End")
end


main()

As main() implementation from the example does not return a value, technically it would be a procedure instead of a function. In many programming languages, main() returns an integer value used as a code to inform whether the process ended successfully (or if it failed). This is useful to create shell scripts. To learn how to create programs for the command line, you can refer to Learn Programming: Command Line Input.

Subroutines for Coins and Dice in GDScript, JavaScript, Python and Lua

The previous steps can be performed for any modern programming language. The next example implements throw_coin(), throw_dice() and print_coin() in GDScript, JavaScript, Python and Lua, using random_integer() as previously defined. Grammar-wise, throw_die() may be a better name than throw_dice(), though.

extends Node


const HEAD = 0
const TAIL = 1


func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    return randi() % int(maximum + 1 - minimum) + minimum


func random_float():
    return randf()


func throw_coin():
    return (random_integer(0, 1))


func throw_dice():
    return (random_integer(1, 6))


func print_coin(face):
    if (face == HEAD):
        print("Heads")
    else:
        print("Tails")


func _ready():
    randomize()

    print_coin(throw_coin())
    print(throw_dice())
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function random_float() {
    return Math.random()
}


function throw_coin() {
    return (random_integer(0, 1))
}


function throw_dice() {
    return (random_integer(1, 6))
}


function print_coin(face) {
    if (face === HEAD) {
        console.log("Heads")
    } else {
        console.log("Tails")
    }
}

function main() {
    print_coin(throw_coin())
    console.log(throw_dice())
}


main()
import random

from typing import Final


HEAD: Final = 0
TAIL: Final = 1


def random_integer(inclusive_minimum, inclusive_maximum):
    return random.randint(inclusive_minimum, inclusive_maximum)


def random_float():
    return random.random()


def throw_coin():
    return (random_integer(0, 1))


def throw_dice():
    return (random_integer(1, 6))


def print_coin(face):
    if (face == HEAD):
        print("Heads")
    else:
        print("Tails")


def main():
    random.seed()

    print_coin(throw_coin())
    print(throw_dice())


if (__name__ == "__main__"):
    main()
local HEAD = 0
local TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum)
    return math.random(inclusive_minimum, inclusive_maximum)
end


function random_float()
    return math.random()
end


function throw_coin()
    return (random_integer(0, 1))
end


function throw_dice()
    return (random_integer(1, 6))
end


function print_coin(face)
    if (face == HEAD) then
        print("Heads")
    else
        print("Tails")
    end
end


function main()
    math.randomseed(os.time())

    print_coin(throw_coin())
    print(throw_dice())
end


main()

In the implementations, throw_dice() draws a value ranging from 1 to 6 to represent the resulting face.

Subroutines: Parameters with Default Value

For a more sophisticated die, the number of faces could be a parameter. Thus, it could draw a value between 1 and the desired number of faces.

In particular, it is also possible to define a default value for a parameter, as it has been commented in Learn Programming: Records. If the parameter is omitted on the call, the implementation uses the predefined default value. For instance, throw_dice() could use 6 as the default value. On the other hand, a call with a parameter such as throw_dice(20) would draw a value between 1 and 20.

extends Node


const HEAD = 0
const TAIL = 1


func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    return randi() % int(maximum + 1 - minimum) + minimum


func random_float():
    return randf()


func throw_coin():
    return (random_integer(0, 1))


func throw_dice(faces = 6):
    return (random_integer(1, faces))


func print_coin(face):
    if (face == HEAD):
        print("Heads")
    else:
        print("Tails")


func _ready():
    randomize()

    print_coin(throw_coin())
    print(throw_dice())
    print(throw_dice(20))
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function random_float() {
    return Math.random()
}


function throw_coin() {
    return (random_integer(0, 1))
}


function throw_dice(faces = 6) {
    return (random_integer(1, faces))
}


function print_coin(face) {
    if (face === HEAD) {
        console.log("Heads")
    } else {
        console.log("Tails")
    }
}


function main() {
    print_coin(throw_coin())
    console.log(throw_dice())
    console.log(throw_dice(20))
}


main()
import random

from typing import Final


HEAD: Final = 0
TAIL: Final = 1


def random_integer(inclusive_minimum, inclusive_maximum):
    return random.randint(inclusive_minimum, inclusive_maximum)


def random_float():
    return random.random()


def throw_coin():
    return (random_integer(0, 1))


def throw_dice(faces = 6):
    return (random_integer(1, faces))


def print_coin(face):
    if (face == HEAD):
        print("Heads")
    else:
        print("Tails")


def main():
    random.seed()

    print_coin(throw_coin())
    print(throw_dice())
    print(throw_dice(20))


if (__name__ == "__main__"):
    main()
local HEAD = 0
local TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum)
    return math.random(inclusive_minimum, inclusive_maximum)
end


function random_float()
    return math.random()
end


function throw_coin()
    return (random_integer(0, 1))
end


function throw_dice(faces)
    faces = faces or 6
    return (random_integer(1, faces))
end


function print_coin(face)
    if (face == HEAD) then
        print("Heads")
    else
        print("Tails")
    end
end


function main()
    math.randomseed(os.time())

    print_coin(throw_coin())
    print(throw_dice())
    print(throw_dice(20))
end


main()

As Lua does not allow defining a default value at the signature of the subroutine, the use of an or allows changing the value if it is omitted at the call. The received value would be nil; as nil in an or results in false, the expression will receive the value defined in the beginning of the implementation (assuming no other value is provided).

Loops: Throwing Coins and Dice Thousands of Times

Finally, to throw coins and dice thousands of times, Repetition Structures (or Loops) can be used. In particular, one could use an accumulator to count results of interest.

The next examples count the number of occurrences of heads and tails after flipping a coin 10000 times.

extends Node


const HEAD = 0
const TAIL = 1


func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    return randi() % int(maximum + 1 - minimum) + minimum


func random_float():
    return randf()


func throw_coin():
    return (random_integer(0, 1))


func throw_dice(faces = 6):
    return (random_integer(1, faces))


func print_coin(face):
    if (face == HEAD):
        print("Heads")
    else:
        print("Tails")


func _ready():
    randomize()

    var heads_count = 0
    var tails_count = 0
    for i in range(10000):
        if (throw_coin() == HEAD):
            heads_count += 1
        else:
            tails_count += 1

    print("Total Heads: " + str(heads_count))
    print("Total Tails: " + str(tails_count))
const HEAD = 0
const TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function random_float() {
    return Math.random()
}


function throw_coin() {
    return (random_integer(0, 1))
}


function throw_dice(faces = 6) {
    return (random_integer(1, faces))
}


function print_coin(face) {
    if (face === HEAD) {
        console.log("Heads")
    } else {
        console.log("Tails")
    }
}


function main() {
    let heads_count = 0
    var tails_count = 0
    for (let i = 0; i < 10000; ++i) {
        if (throw_coin() === HEAD) {
            ++heads_count
        } else {
            ++tails_count
        }
    }

    console.log("Total Heads: " + heads_count)
    console.log("Total Tails: " + tails_count)
}


main()
import random

from typing import Final


HEAD: Final = 0
TAIL: Final = 1


def random_integer(inclusive_minimum, inclusive_maximum):
    return random.randint(inclusive_minimum, inclusive_maximum)


def random_float():
    return random.random()


def throw_coin():
    return (random_integer(0, 1))


def throw_dice(faces = 6):
    return (random_integer(1, faces))


def print_coin(face):
    if (face == HEAD):
        print("Heads")
    else:
        print("Tails")


def main():
    random.seed()

    heads_count = 0
    tails_count = 0
    for i in range(10000):
        if (throw_coin() == HEAD):
            heads_count += 1
        else:
            tails_count += 1

    print("Total Heads: " + str(heads_count))
    print("Total Tails: " + str(tails_count))


if (__name__ == "__main__"):
    main()
local HEAD = 0
local TAIL = 1


function random_integer(inclusive_minimum, inclusive_maximum)
    return math.random(inclusive_minimum, inclusive_maximum)
end


function random_float()
    return math.random()
end


function throw_coin()
    return (random_integer(0, 1))
end


function throw_dice(faces)
    faces = faces or 6
    return (random_integer(1, faces))
end


function print_coin(face)
    if (face == HEAD) then
        print("Heads")
    else
        print("Tails")
    end
end


function main()
    math.randomseed(os.time())

    local heads_count = 0
    local tails_count = 0
    for i = 1, 10000 do
        if (throw_coin() == HEAD) then
            heads_count = heads_count + 1
        else
            tails_count = tails_count + 1
        end
    end

    print("Total Heads: " .. heads_count)
    print("Total Tails: " .. tails_count)
end


main()
Representation of the code with the repetition structure as a flowchart in Flowgorithm.

Run the code of the project a few times and check the results. The values of heads and tails should be close in most cases.

For an better approach, you can add a repetition structure to run the simulation several times. The next code blocks suggest how to update the entry point to illustrate the approach.

func _ready():
    randomize()

    for j in range(10):
        var heads_count = 0
        var tails_count = 0
        for i in range(10000):
            if (throw_coin() == HEAD):
                heads_count += 1
            else:
                tails_count += 1

        print("Total Heads: " + str(heads_count))
        print("Total Tails: " + str(tails_count))
        print()
function main() {
    for (let j = 0; j < 10; ++j) {
        let heads_count = 0
        let tails_count = 0
        for (let i = 0; i < 10000; ++i) {
            if (throw_coin() === HEAD) {
                ++heads_count
            } else {
                ++tails_count
            }
        }

        console.log("Total Heads: " + heads_count)
        console.log("Total Tails: " + tails_count)
        console.log()
    }
}
def main():
    random.seed()

    for j in range(10):
        heads_count = 0
        tails_count = 0
        for i in range(10000):
            if (throw_coin() == HEAD):
                heads_count += 1
            else:
                tails_count += 1

        print("Total Heads: " + str(heads_count))
        print("Total Tails: " + str(tails_count))
        print()
function main()
    math.randomseed(os.time())

    for j = 1, 10 do
        local heads_count = 0
        local tails_count = 0
        for i = 1, 10000 do
            if (throw_coin() == HEAD) then
                heads_count = heads_count + 1
            else
                tails_count = tails_count + 1
            end
        end

        print("Total Heads: " .. heads_count)
        print("Total Tails: " .. tails_count)
        print()
    end
end

In the future, we will repeat simulations hundreds or thousands of times to create Monte Carlo Simulations. In this topic, the repetition are only a curiosity to practice using repetition structures.

Drawing Primitives: Squares and Rectangles

To draw a coin, one could draw a circle or an arc with a number inside it, representing the resulting face. Analogously, the drawing of a die could use a square with circles or a number inside it.

Besides Points, Lines, and Arcs (that we already know how to draw), drawing primitives typically include rectangles and squares. Before using them, it can be worth implementing our own to understand how they can be implemented.

HTML Canvas for JavaScript

As in the Introduction of Ideas, Rules, Simulation, JavaScript requires an auxiliary HTML file to declare the canvas. You can choose the name of the HTML file (for instance, index.html). The follow example assumes that the JavaScript fill is named script.js and the canvas will have the identifier (id) canvas. If you change the values, remember to modify them in the files as needed.

<!DOCTYPE html>
<html lang="pt-BR">
  <head>
    <meta charset="utf-8">
    <title>www.FrancoGarcia.com</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <div style="text-align: center;">
      <canvas id="canvas"
              width="1"
              height="1"
              style="border: 1px solid #000000;">
      >
          Accessibility: alternative text for graphical content.
      </canvas>
    </div>

    <!-- NOTE Updated with the name of the JavaScript file. -->
    <script src="./script.js"></script>
  </body>
</html>

The file also keeps the reminder about accessibility. In this topic, the content will become even more inaccessible for people with certain vision disabilities, due to the addition of graphical content without alternatives to convey the contents.

In the future, the intention is discussing ways to make simulations more accessible. At this time, this reminder is only informative, to raise awareness of the importance of accessibility.

Drawing Squares and Rectangles

It is simple to draw a rectangle or a square when one already knows how to draw pixels: it suffices to repeat drawing lines in adjacent pixels. Another way to understand the idea consists of thinking about a line as a rectangle on which one of the measurements (width or height) is equal to 1 pixel.

Thus, to draw a colored rectangle, it is sufficient to use a repetition structure that draws a horizontal line for each pixel of the desired height. The procedure franco_draw_rectangle() implements this approach.

To draw a square, one can draw a rectangle with equal values to the width and height, as performed in franco_draw_square().

# Root must be a Node that allows drawing using _draw().
extends Control


const WIDTH = 320
const HEIGHT = 240
const BLACK = Color.black
const WHITE = Color.white


func franco_draw_line(x0, y0, x1, y1, color):
    draw_line(Vector2(x0, y0), Vector2(x1, y1), color)


func franco_draw_rectangle(x, y, width, height, color):
    var end_x = x + width
    var end_y = y + height
    for current_y in range(y, end_y):
        franco_draw_line(x, current_y,
                         end_x, current_y,
                         color)


func franco_draw_square(x, y, side, color):
    franco_draw_rectangle(x, y, side, side, color)


func _ready():
    randomize()

    OS.set_window_size(Vector2(WIDTH, HEIGHT))
    OS.set_window_title("Olá, meu nome é Franco!")


func _draw():
    VisualServer.set_default_clear_color(BLACK)

    franco_draw_rectangle(10, 20, 80, 40, WHITE)

    franco_draw_rectangle(10, 100, 50, 50, WHITE)
    franco_draw_square(100, 100, 50, WHITE)
const WIDTH = 320
const HEIGHT = 240
const BLACK = "black"
const WHITE = "white"


let canvas = document.getElementById("canvas")
let context = canvas.getContext("2d")


function franco_draw_line(x0, y0, x1, y1, color) {
    context.strokeStyle = color
    // context.fillStyle = color
    context.beginPath()
    context.moveTo(x0, y0)
    context.lineTo(x1, y1)
    context.closePath()
    context.stroke()
}


function franco_draw_rectangle(x, y, width, height, color) {
    let end_x = x + width
    let end_y = y + height
    for (let current_y = y; current_y < end_y; ++current_y) {
        franco_draw_line(x, current_y,
                         end_x, current_y,
                         color)
    }
}


function franco_draw_square(x, y, side, color) {
    franco_draw_rectangle(x, y, side, side, color)
}


function draw() {
    context.clearRect(0, 0, WIDTH, HEIGHT)
    context.fillStyle = BLACK
    context.fillRect(0, 0, WIDTH, HEIGHT)

    franco_draw_rectangle(10, 20, 80, 40, WHITE)

    franco_draw_rectangle(10, 100, 50, 50, WHITE)
    franco_draw_square(100, 100, 50, WHITE)
}


function main() {
    document.title = "Olá, meu nome é Franco!"

    canvas.width = WIDTH
    canvas.height = HEIGHT

    draw()
}


main()
import pygame
import math
import random
import sys

from pygame import gfxdraw
from typing import Final


WIDTH: Final = 320
HEIGHT: Final = 240
WHITE: Final = (255, 255, 255)
BLACK: Final = (0, 0, 0)


pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Olá, meu nome é Franco!")


def franco_draw_line(x0, y0, x1, y1, color):
    pygame.draw.line(window, color, (x0, y0), (x1, y1))


def franco_draw_rectangle(x, y, width, height, color):
    end_x = x + width
    end_y = y + height
    for current_y in range(y, end_y):
        franco_draw_line(x, current_y,
                         end_x, current_y,
                         color)


def franco_draw_square(x, y, side, color):
    franco_draw_rectangle(x, y, side, side, color)


def main():
    random.seed()

    while (True):
        for event in pygame.event.get():
            if (event.type == pygame.QUIT):
                pygame.quit()
                sys.exit(0)

        window.fill(BLACK)

        franco_draw_rectangle(10, 20, 80, 40, WHITE)

        franco_draw_rectangle(10, 100, 50, 50, WHITE)
        franco_draw_square(100, 100, 50, WHITE)

        pygame.display.flip()


if (__name__ == "__main__"):
    main()
local WIDTH = 320
local HEIGHT = 240
local BLACK = {0.0, 0.0, 0.0}
local WHITE = {1.0, 1.0, 1.0}


function franco_draw_line(x0, y0, x1, y1, color)
    love.graphics.setColor(color)
    love.graphics.line(x0, y0, x1, y1)
end


function franco_draw_rectangle(x, y, width, height, color)
    local end_x = x + width
    local end_y = y + height
    for current_y = y, end_y - 1 do
        franco_draw_line(x, current_y,
                         end_x, current_y,
                         color)
    end
end


function franco_draw_square(x, y, side, color)
    franco_draw_rectangle(x, y, side, side, color)
end


function love.load()
    math.randomseed(os.time())

    love.window.setMode(WIDTH, HEIGHT)
    love.window.setTitle("Olá, meu nome é Franco!")
end


function love.draw()
    love.graphics.setBackgroundColor(BLACK)

    franco_draw_rectangle(10, 20, 80, 40, WHITE)

    franco_draw_rectangle(10, 100, 50, 50, WHITE)
    franco_draw_square(100, 100, 50, WHITE)
end

The resulting drawing is shown in the next canvas.

A rectangle and two squares, all white, drawn over a black background using lines.

If one wished to only draw the stroke of a rectangle, she/he could draw the four lines that delimit the shape.

Drawing Primitives for Squares and Rectangles

As it has happened in Ideas, Rules, Simulation: Pixels and Drawing Primitives (Points, Lines, and Arcs), drawing APIs typically provide subroutines to draw rectangles. In fact:

  • Godot provides draw_rect();
  • JavaScript with canvas provides fillRect() (which has been used previously to draw the background color);
  • Python with PyGame has pygame.draw.rect();
  • Lua with LÖVE defines love.graphics.rectangle().

On the other hand, as a square is a rectangle, not every API provides a subroutine to draw a square. Thus, franco_draw_square() will draw a square using the primitive to draw rectangles. It would also be possible to keep the previous implementation, using franco_draw_rectangle() on the implementation of franco_draw_square() to draw a square.

# Root must be a Node that allows drawing using _draw().
extends Control


const WIDTH = 320
const HEIGHT = 240
const BLACK = Color.black
const WHITE = Color.white


func franco_draw_rectangle(x, y, width, height, color):
    draw_rect(Rect2(x, y, width, height), color, true)


func franco_draw_square(x, y, side, color):
    draw_rect(Rect2(x, y, side, side), color)


func _ready():
    randomize()

    OS.set_window_size(Vector2(WIDTH, HEIGHT))
    OS.set_window_title("Olá, meu nome é Franco!")


func _draw():
    VisualServer.set_default_clear_color(BLACK)

    franco_draw_rectangle(10, 20, 80, 40, WHITE)

    franco_draw_rectangle(10, 100, 50, 50, WHITE)
    franco_draw_square(100, 100, 50, WHITE)
const WIDTH = 320
const HEIGHT = 240
const BLACK = "black"
const WHITE = "white"


let canvas = document.getElementById("canvas")
let context = canvas.getContext("2d")


function franco_draw_rectangle(x, y, width, height, color) {
    context.fillStyle = color
    context.fillRect(x, y, width, height)
}


function franco_draw_square(x, y, side, color) {
    context.fillStyle = color
    context.fillRect(x, y, side, side)
}


function draw() {
    context.clearRect(0, 0, WIDTH, HEIGHT)
    context.fillStyle = BLACK
    context.fillRect(0, 0, WIDTH, HEIGHT)

    franco_draw_rectangle(10, 20, 80, 40, WHITE)

    franco_draw_rectangle(10, 100, 50, 50, WHITE)
    franco_draw_square(100, 100, 50, WHITE)
}


function main() {
    document.title = "Olá, meu nome é Franco!"

    canvas.width = WIDTH
    canvas.height = HEIGHT

    draw()
}


main()
import pygame
import math
import random
import sys

from pygame import gfxdraw
from typing import Final


WIDTH: Final = 320
HEIGHT: Final = 240
WHITE: Final = (255, 255, 255)
BLACK: Final = (0, 0, 0)


pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Olá, meu nome é Franco!")


def franco_draw_rectangle(x, y, width, height, color):
    pygame.draw.rect(window, color, (x, y, width, height))


def franco_draw_square(x, y, side, color):
    pygame.draw.rect(window, color, (x, y, side, side))


def main():
    random.seed()

    while (True):
        for event in pygame.event.get():
            if (event.type == pygame.QUIT):
                pygame.quit()
                sys.exit(0)

        window.fill(BLACK)

        franco_draw_rectangle(10, 20, 80, 40, WHITE)

        franco_draw_rectangle(10, 100, 50, 50, WHITE)
        franco_draw_square(100, 100, 50, WHITE)

        pygame.display.flip()


if (__name__ == "__main__"):
    main()
local WIDTH = 320
local HEIGHT = 240
local BLACK = {0.0, 0.0, 0.0}
local WHITE = {1.0, 1.0, 1.0}


function franco_draw_rectangle(x, y, width, height, color)
    love.graphics.setColor(color)
    love.graphics.rectangle("fill", x, y, width, height)
end


function franco_draw_square(x, y, side, color)
    love.graphics.setColor(color)
    love.graphics.rectangle("fill", x, y, side, side)
end


function love.load()
    math.randomseed(os.time())

    love.window.setMode(WIDTH, HEIGHT)
    love.window.setTitle("Olá, meu nome é Franco!")
end


function love.draw()
    love.graphics.setBackgroundColor(BLACK)

    franco_draw_rectangle(10, 20, 80, 40, WHITE)

    franco_draw_rectangle(10, 100, 50, 50, WHITE)
    franco_draw_square(100, 100, 50, WHITE)
end

The resulting drawing is shown in the next canvas.

A rectangle and two squares, all white, drawn over a black background using drawing primitives.

Thus, the current inventory of drawing primitives include pixels (points), lines, arcs, rectangles and square. In other words, we already have everything that is needed for this topic.

Drawing Coins

It is time to use your artistic skills to draw the results of a coin flip. It is likely that your artistic skills are enough to create a better drawing than the author's one.

In the next example, the goal is drawing the results of throwing a coin (in other words, head or tail) inside an arc. To do this, we can combine parts of the code of previous examples to simulate flipping a coin, then generate a drawing for heads or tails. The drawing will replace the text and the emoji in the console with simple graphics on a window.

It is important to generate a single value at a time, to ensure that the result is not changed when redrawing the window. In the code, the result of the random drawing is stored in result.

The longest part of the implementation is the code to draw each face of the coin. The use of values as a function (as a proportion) of WIDTH and HEIGHT allows resizing the drawing if the value of the constants is changed. To improve the code, the values could be updated when resizing the window manually when the project is running. For a simpler version, one could keep a fixed size for the window and use specific values for the drawings.

# Root must be a Node that allows drawing using _draw().
extends Control


const WIDTH = 320
const HEIGHT = 240
const BLACK = Color.black
const WHITE = Color.white
const HEAD = 0
const TAIL = 1


var result = null


func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    return randi() % int(maximum + 1 - minimum) + minimum



func franco_draw_line(x0, y0, x1, y1, color):
    draw_line(Vector2(x0, y0), Vector2(x1, y1), color)



const POINT_COUNT = 100
func franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color):
    draw_arc(Vector2(center_x, center_y),
                radius,
                start_angle, end_angle,
                POINT_COUNT,
                color)


func throw_coin():
    return (random_integer(0, 1))


func _ready():
    randomize()

    OS.set_window_size(Vector2(WIDTH, HEIGHT))
    OS.set_window_title("Olá, meu nome é Franco!")

    result = throw_coin()


func _draw():
    VisualServer.set_default_clear_color(BLACK)

    if (result == HEAD):
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 2 * PI, WHITE)
        franco_draw_arc(0.6 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, WHITE)
        franco_draw_arc(0.4 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, WHITE)
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.20 * HEIGHT, 0,     PI, WHITE)
    else:
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 2 * PI, WHITE)

        franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.60 * HEIGHT, WHITE)

        franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.40 * WIDTH, 0.35 * HEIGHT, WHITE)
        franco_draw_line(0.40 * WIDTH, 0.35 * HEIGHT, 0.45 * WIDTH, 0.50 * HEIGHT, WHITE)
        franco_draw_line(0.45 * WIDTH, 0.50 * HEIGHT, 0.50 * WIDTH, 0.35 * HEIGHT, WHITE)

        franco_draw_line(0.60 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.35 * HEIGHT, WHITE)
        franco_draw_line(0.60 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
        franco_draw_line(0.50 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
const WIDTH = 320
const HEIGHT = 240
const BLACK = "black"
const WHITE = "white"
const HEAD = 0
const TAIL = 1


let canvas = document.getElementById("canvas")
let context = canvas.getContext("2d")


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function franco_draw_line(x0, y0, x1, y1, color) {
    context.strokeStyle = color
    // context.fillStyle = color
    context.beginPath()
    context.moveTo(x0, y0)
    context.lineTo(x1, y1)
    context.closePath()
    context.stroke()
}


function franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color) {
    context.strokeStyle = color
    context.beginPath()
    context.arc(center_x, center_y,
                radius,
                start_angle, end_angle)
    context.stroke()
    context.closePath()
}


function throw_coin() {
    return (random_integer(0, 1))
}


function draw() {
    context.clearRect(0, 0, WIDTH, HEIGHT)
    context.fillStyle = BLACK
    context.fillRect(0, 0, WIDTH, HEIGHT)

    let result = throw_coin()
    if (result === HEAD) {
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 2 * Math.PI, WHITE)
        franco_draw_arc(0.6 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, WHITE)
        franco_draw_arc(0.4 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, WHITE)
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.20 * HEIGHT, 0,     Math.PI, WHITE)
    } else {
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 2 * Math.PI, WHITE)

        franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.60 * HEIGHT, WHITE)

        franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.40 * WIDTH, 0.35 * HEIGHT, WHITE)
        franco_draw_line(0.40 * WIDTH, 0.35 * HEIGHT, 0.45 * WIDTH, 0.50 * HEIGHT, WHITE)
        franco_draw_line(0.45 * WIDTH, 0.50 * HEIGHT, 0.50 * WIDTH, 0.35 * HEIGHT, WHITE)

        franco_draw_line(0.60 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.35 * HEIGHT, WHITE)
        franco_draw_line(0.60 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
        franco_draw_line(0.50 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
    }
}


function main() {
    document.title = "Olá, meu nome é Franco!"

    canvas.width = WIDTH
    canvas.height = HEIGHT

    draw()
}


main()
import pygame
import math
import random
import sys

from pygame import gfxdraw
from typing import Final


WIDTH: Final = 320
HEIGHT: Final = 240
WHITE: Final = (255, 255, 255)
BLACK: Final = (0, 0, 0)
HEAD: Final = 0
TAIL: Final = 1


pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Olá, meu nome é Franco!")


def random_integer(inclusive_minimum, inclusive_maximum):
    return random.randint(inclusive_minimum, inclusive_maximum)


def franco_draw_line(x0, y0, x1, y1, color):
    pygame.draw.line(window, color, (x0, y0), (x1, y1))


def franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color):
    pygame.gfxdraw.arc(window,
                       int(center_x), int(center_y),
                       int(radius),
                       int(math.degrees(start_angle)), int(math.degrees(end_angle)),
                       color)


def throw_coin():
    return (random_integer(0, 1))


def main():
    random.seed()

    result = throw_coin()
    while (True):
        for event in pygame.event.get():
            if (event.type == pygame.QUIT):
                pygame.quit()
                sys.exit(0)

        window.fill(BLACK)

        if (result == HEAD):
            franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 1.999 * math.pi, WHITE)
            franco_draw_arc(0.6 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, WHITE)
            franco_draw_arc(0.4 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, WHITE)
            franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.20 * HEIGHT, 0,         math.pi, WHITE)
        else:
            franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 1.999 * math.pi, WHITE)

            franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.60 * HEIGHT, WHITE)

            franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.40 * WIDTH, 0.35 * HEIGHT, WHITE)
            franco_draw_line(0.40 * WIDTH, 0.35 * HEIGHT, 0.45 * WIDTH, 0.50 * HEIGHT, WHITE)
            franco_draw_line(0.45 * WIDTH, 0.50 * HEIGHT, 0.50 * WIDTH, 0.35 * HEIGHT, WHITE)

            franco_draw_line(0.60 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.35 * HEIGHT, WHITE)
            franco_draw_line(0.60 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
            franco_draw_line(0.50 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)

        pygame.display.flip()


if (__name__ == "__main__"):
    main()
local WIDTH = 320
local HEIGHT = 240
local BLACK = {0.0, 0.0, 0.0}
local WHITE = {1.0, 1.0, 1.0}
local HEAD = 0
local TAIL = 1


local result


function random_integer(inclusive_minimum, inclusive_maximum)
    return math.random(inclusive_minimum, inclusive_maximum)
end


function franco_draw_line(x0, y0, x1, y1, color)
    love.graphics.setColor(color)
    love.graphics.line(x0, y0, x1, y1)
end


local POINT_COUNT = 100
function franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color)
    love.graphics.setColor(color)
    love.graphics.arc("line", "open",
                      center_x, center_y,
                      radius,
                      start_angle, end_angle,
                      POINT_COUNT)
end


function throw_coin()
    return (random_integer(0, 1))
end


function love.load()
    math.randomseed(os.time())

    love.window.setMode(WIDTH, HEIGHT)
    love.window.setTitle("Olá, meu nome é Franco!")

    result = throw_coin()
end


function love.draw()
    love.graphics.setBackgroundColor(BLACK)

    if (result == HEAD) then
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 2 * math.pi, WHITE)
        franco_draw_arc(0.6 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, WHITE)
        franco_draw_arc(0.4 * WIDTH, 0.4 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, WHITE)
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.20 * HEIGHT, 0,     math.pi, WHITE)
    else
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.33 * HEIGHT, 0, 2 * math.pi, WHITE)

        franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.60 * HEIGHT, WHITE)

        franco_draw_line(0.40 * WIDTH, 0.60 * HEIGHT, 0.40 * WIDTH, 0.35 * HEIGHT, WHITE)
        franco_draw_line(0.40 * WIDTH, 0.35 * HEIGHT, 0.45 * WIDTH, 0.50 * HEIGHT, WHITE)
        franco_draw_line(0.45 * WIDTH, 0.50 * HEIGHT, 0.50 * WIDTH, 0.35 * HEIGHT, WHITE)

        franco_draw_line(0.60 * WIDTH, 0.60 * HEIGHT, 0.60 * WIDTH, 0.35 * HEIGHT, WHITE)
        franco_draw_line(0.60 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
        franco_draw_line(0.50 * WIDTH, 0.35 * HEIGHT, 0.55 * WIDTH, 0.50 * HEIGHT, WHITE)
    end
end

A possible result of the drawing is displayed on the next canvas.

Simulation of flipping coins, with drawings for the results of heads and tails.

The example implemented for the canvas canvas adds an animation updated every second. Thus, if you wait some time, you should see both results. The time of the transition between the images can vary, because it uses random results for the coin.

In all versions, the drawing of the external circle could be moved before the conditional structure, because it is performed for both cases. You can do that change to verify the statement.

The Python version with PyGame approximates 2 * math.pi due to a limitation of pygame.gfxdraw.arc() commented in Pixels and Drawing Primitives (Points, Lines, and Arcs). After we introduce a subroutine to draw circles, we will be able to use it instead of drawing arcs.

Drawing Dice

A drawing for a die can use a square and a rectangle for the outlines. For the number, one can either draw the value using arcs or circles, or write it as text.

The next example draws the value with arcs (forming circles). The code draws the number of circles corresponding to the random number that was drawn. Odd values draw a circle at the central position of the window. Values that are larger than 1 distribute circles in two (even values) or three columns (odd values).

# Root must be a Node that allows drawing using _draw().
extends Control


const WIDTH = 320
const HEIGHT = 240
const BLACK = Color.black
const WHITE = Color.white


var result = null


func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    return randi() % int(maximum + 1 - minimum) + minimum


func franco_draw_rectangle(x, y, width, height, color):
    draw_rect(Rect2(x, y, width, height), color, true)


const POINT_COUNT = 100
func franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color):
    draw_arc(Vector2(center_x, center_y),
                radius,
                start_angle, end_angle,
                POINT_COUNT,
                color)


func throw_dice(faces = 6):
    return (random_integer(1, faces))


func _ready():
    randomize()

    OS.set_window_size(Vector2(WIDTH, HEIGHT))
    OS.set_window_title("Olá, meu nome é Franco!")

    result = throw_dice(6)


func _draw():
    VisualServer.set_default_clear_color(BLACK)

    franco_draw_rectangle(0.1 * WIDTH, 0.1 * HEIGHT, 0.8 * WIDTH, 0.8 * HEIGHT, WHITE)

    if (result == 1):
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
    elif (result == 2):
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
    elif (result == 3):
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
    elif (result == 4):
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
    elif (result == 5):
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
    else:
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * PI, BLACK)
const WIDTH = 320
const HEIGHT = 240
const BLACK = "black"
const WHITE = "white"


let canvas = document.getElementById("canvas")
let context = canvas.getContext("2d")


function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}


function franco_draw_rectangle(x, y, width, height, color) {
    context.fillStyle = color
    context.fillRect(x, y, width, height)
}


function franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color) {
    context.strokeStyle = color
    context.beginPath()
    context.arc(center_x, center_y,
                radius,
                start_angle, end_angle)
    context.stroke()
    context.closePath()
}


function throw_dice(faces = 6) {
    return (random_integer(1, faces))
}


function draw() {
    context.clearRect(0, 0, WIDTH, HEIGHT)
    context.fillStyle = BLACK
    context.fillRect(0, 0, WIDTH, HEIGHT)

    let result = throw_dice(6)

    franco_draw_rectangle(0.1 * WIDTH, 0.1 * HEIGHT, 0.8 * WIDTH, 0.8 * HEIGHT, WHITE)

    if (result == 1) {
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
    } else if (result == 2) {
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
    } else if (result == 3) {
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
    } else if (result == 4) {
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
    } else if (result == 5) {
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
    } else {
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * Math.PI, BLACK)
    }
}


function main() {
    document.title = "Olá, meu nome é Franco!"

    canvas.width = WIDTH
    canvas.height = HEIGHT

    draw()
}


main()
import pygame
import math
import random
import sys

from pygame import gfxdraw
from typing import Final


WIDTH: Final = 320
HEIGHT: Final = 240
WHITE: Final = (255, 255, 255)
BLACK: Final = (0, 0, 0)


pygame.init()
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Olá, meu nome é Franco!")


def random_integer(inclusive_minimum, inclusive_maximum):
    return random.randint(inclusive_minimum, inclusive_maximum)


def franco_draw_rectangle(x, y, width, height, color):
    pygame.draw.rect(window, color, (x, y, width, height))


def franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color):
    pygame.gfxdraw.arc(window,
                       int(center_x), int(center_y),
                       int(radius),
                       int(math.degrees(start_angle)), int(math.degrees(end_angle)),
                       color)


def throw_dice(faces = 6):
    return (random_integer(1, faces))


def main():
    random.seed()

    result = throw_dice(6)
    while (True):
        for event in pygame.event.get():
            if (event.type == pygame.QUIT):
                pygame.quit()
                sys.exit(0)

        window.fill(BLACK)

        franco_draw_rectangle(0.1 * WIDTH, 0.1 * HEIGHT, 0.8 * WIDTH, 0.8 * HEIGHT, WHITE)

        if (result == 1):
            franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
        elif (result == 2):
            franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
        elif (result == 3):
            franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
        elif (result == 4):
            franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
        elif (result == 5):
            franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
        else:
            franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)
            franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 1.999 * math.pi, BLACK)

        pygame.display.flip()


if (__name__ == "__main__"):
    main()
local WIDTH = 320
local HEIGHT = 240
local BLACK = {0.0, 0.0, 0.0}
local WHITE = {1.0, 1.0, 1.0}
local HEAD = 0
local TAIL = 1


local result


function random_integer(inclusive_minimum, inclusive_maximum)
    return math.random(inclusive_minimum, inclusive_maximum)
end


function franco_draw_rectangle(x, y, width, height, color)
    love.graphics.setColor(color)
    love.graphics.rectangle("fill", x, y, width, height)
end


local POINT_COUNT = 100
function franco_draw_arc(center_x, center_y, radius, start_angle, end_angle, color)
    love.graphics.setColor(color)
    love.graphics.arc("line", "open",
                      center_x, center_y,
                      radius,
                      start_angle, end_angle,
                      POINT_COUNT)
end


function throw_dice(faces)
    faces = faces or 6
    return (random_integer(1, faces))
end


function love.load()
    math.randomseed(os.time())

    love.window.setMode(WIDTH, HEIGHT)
    love.window.setTitle("Olá, meu nome é Franco!")

    result = throw_dice(6)
end


function love.draw()
    love.graphics.setBackgroundColor(BLACK)

    franco_draw_rectangle(0.1 * WIDTH, 0.1 * HEIGHT, 0.8 * WIDTH, 0.8 * HEIGHT, WHITE)

    if (result == 1) then
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    elseif (result == 2) then
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    elseif (result == 3) then
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    elseif (result == 4) then
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    elseif (result == 5) then
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    else
        franco_draw_arc(0.3 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.3 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        franco_draw_arc(0.7 * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    end
end

A possible result of the drawing is displayed on the next canvas.

Simulation of rolling a die with six faces, with drawings of circles of each possible result. The number of drawn circles corresponds to the value that has been drawn.

The canvas draws values every second to show different results over time. Except in the case of a generator with a degenerated distribution, or if one knows the seed and the algorithm to generate the numbers, the next value will be a surprise. It will be between 1 and 6, though what will the value be?

The implementation of the example is quite simple. In fact, it contains duplicated code, that could be removed with a more elegant solution.

The solution would also be more difficult to read and understand. To avoid introducing unnecessary complexity in these first topics of Ideas, Rules, Simulation, the author will keep the duplicated lines in the code of the example. The most concise solution is not always the most readable one.

If you wish to practice and remove duplicated lines, there are different approaches to change the code. For instance, you could draw arcs at every position depending on the number, or use a repetition structure to increment offsets to move between each arc.

Show/Hide a Possible Solution in Lua

function love.draw()
    love.graphics.setBackgroundColor(BLACK)

    franco_draw_rectangle(0.1 * WIDTH, 0.1 * HEIGHT, 0.8 * WIDTH, 0.8 * HEIGHT, WHITE)

    if ((result == 1) or (result == 3) or (result == 5)) then
        franco_draw_arc(0.5 * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
    end

    if ((result == 2) or (result == 3) or (result == 6)) then
        for i = 0.3, 0.7, 0.4 do
            franco_draw_arc(i * WIDTH, 0.5 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        end
    end

    if ((result == 4) or (result == 5) or (result == 6)) then
        for i = 0.3, 0.7, 0.4 do
            franco_draw_arc(i * WIDTH, 0.3 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
            franco_draw_arc(i * WIDTH, 0.7 * HEIGHT, 0.05 * HEIGHT, 0, 2 * math.pi, BLACK)
        end
    end
end

A more advanced solution could map the values in a 3x3 matrix, drawing values marked to be drawn on the matrix. This solution would be simpler to read and understand, though data structure will be a future topic in Ideas, Rules, Simulation. If you have already studied Learn Programming: Arrays, Strings, Collections and Data Structures, you can try to implement it.

Concepts Covered From Learn Programming

Concepts from Learn Programming covered in this topic:

  1. Entry point;
  2. Output;
  3. Data types;
  4. Variables and constants;
  5. Arithmetic;
  6. Relational operations and comparisons;
  7. Logic operations;
  8. Conditional structures;
  9. Subroutines (functions and procedures);
  10. Repetition structures (or loops);
  11. Libraries.

It is worth noticing that even drawing primitives can use almost every basic programming concept.

Once again, the values for colors in Lua used tables (dictionaries) as array, described in Collections.

New Items for Your Inventory

Computational Thinking Abilities:

Tools:

  • List of emojis.

Skills:

  • Prototyping simulations using a version for a console (terminal);
  • Drawing rectangles using repetition structures;
  • Drawing rectangles using drawing primitives;
  • Drawings using proportional sizes, exploring the window's width and height.

Concepts:

  • Drawing primitives: rectangles;
  • Drawing primitives: squares;

Programming resources:

  • Drawing rectangles and squares.

Practice

To learn programming, deliberate practice must follow the concepts. Try doing the next exercises to practice.

  1. Change the example of the die to use 8 faces instead of 6. Update the drawing code to support the new results.

  2. Instead of drawing values, write the value as text message inside the circle (coin) or square (die).

    Depending on the programming language, you will need to convert the drawn integer number to a string. To learn how to do that, refer to Learn Programming: Data Types.

    To draw the results as text, refer to Ideas, Rules, Simulation: Introduction (Window and Hello, world!).

  3. Why should you create drawings using values of width and height instead of specific sizes? What are the advantages of such approach? What are the disadvantages of the approach?

  4. If the implementation of the examples use sizes based on the window's height and width, why does not the drawing update automatically when the window is resized while the program is running?

    Tip. The value of the constants is never modified. To consider new values, they would need to be variables that were changed at every modification of the window's dimensions.

Deepening

In Ideas, Rules, Simulation, this section provides complementary content.

Emojis

An emoji é encoded as a character. This is the reason that it was declared as text as part of a string in the example of flipping a coin in the console (terminal) using Lua.

The encoding used for text that supports emojis is called Unicode. It is also used to encode text with accents, as well as other special characters and characters from hundreds of (human) languages. On computer, text is stored as numbers (binary numbers, to be more exact). To learn more about how computers store text, you can refer to Learn Programming: Data Types.

To discover the code of an emoji, you can access this official list.

To create an accessible emoji in HTML for Web content, such as this 👑, it is necessary to use a special tag with a textual description.

<span role="img" aria-label="Emoji of a crown">👑</span>

This way, people who use technologies such as screen readers will be able to understand the content. For instance, this is important for blind people.

Unfortunately, this technique cannot be applied to other programming languages. We shall consider accessibility for simulations on future topics of Ideas, Rules, Simulation.

Next Steps

To program, it is fundamental to dominate computational thinking skills. The material of Learn Programming argues that programming is solving problems. Programming languages are mere tools to implement your solution.

Thus, although this topic could be shorter, the author has chosen to detailed the process of how to write a computer program. The purpose is helping you to become more independent and able to solve problems using computers. After all, the goal is allowing you to create your own systems, applications (apps), and simulations in the future.

To create a simulation, we have converted an idea to computer code by implementing rules to process data. This is achieved by using abstractions for data decomposition (for instance, using variables or records) and functional decomposition (for instance, using subroutines such as functions and procedures).

This time, instead of creating a chaotic drawing, we have used the result from a random draw to illustrate the obtained result. We have abstracted the random draw two values as a coin, and of six values as a die.

However, the drawings would become more appealing if they were colored (instead of being simple strokes). For a better presentation, it would be worth filling the strokes of the arcs with a color to truly draw a circle.

Tools to draw images typically provide a feature represented by a bucket of paint, that fills a region with a color. We can implement it as an algorithm.

Thus, the next topic introduces some additional drawing primitives -- this time, to draw circles, ellipses, and polygons. Both as strokes and as filled shapes.

Furthermore, if you have created a creative or interesting illustration, consider sharing it. Alternatively, if you have found this material useful, you can also share it. If possible, use the hashtags #IdeasRulesSimulation and #FrancoGarciaCom.

I thank you for your attention. See you soon!

Ideas, Rules, Simulation

  1. Motivation;
  2. Introduction: Window and Hello World;
  3. Pixels and drawing primitives (points, lines, and arcs);
  4. Randomness and noise;
  5. Coins and dice, rectangles and squares;
  6. Drawing with drawing primitives (strokes and fillings for circles, ellipses and polygons);
  7. Saving and loading image files;
  8. ...

This material is a work in progress; therefore, if you have arrived early and the previous items do not have links, please return to this page to check out updates.

If you wish to contact me or have any questions, you can chat with me by:

Information about contact and social networks are also available at the footer of every page.

Your opinion about the series will be fundamental to enable me to create a material that is more accessible and simpler for more people.

  • Video
  • Informatics
  • Programming
  • Beginner
  • Computational Thinking
  • Ideas, Rules, Simulation
  • Python
  • PyGame
  • Lua
  • LÖVE (Love2D)
  • Javascript
  • HTML Canvas
  • Godot
  • Gdscript
  • Flowgorithm