Avançar para o conteúdo
Imagem com logotipo, contendo link para a página inicial
  • 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.

Ideias, Regras, Simulação: Moedas, Dados e Retângulos

Ilustrações de imagens representado os possíveis resultados de lançamentos de moedas e dados, resultantes de programas criados para JavaScript com HTML Canvas, GDScript com Godot Engine, Python com PyGame, e Lua com LÖVE. A imagem também apresenta um link para este website: <www.francogarcia.com>, assim como a conta francogarciacom, usada para o Twitter e Instagram do autor.

Créditos para a imagem: Imagem criada pelo autor usando o programa Inkscape; ícones de Font Awesome.

Pré-Requisitos

O material de Ideias, Regras, Simulação promove aprendizado baseado em projetos (simulações interativas e jogos digitais) com recursos multimídia. Assim, ele é complementar ao material de Aprenda Programação, que introduz fundamentos e técnicas básicas de programação, com exemplos para JavaScript, Python, Lua e GDScript (para Godot Engine).

Para Ideias, Regras, Simulação, você precisará de um ambiente de desenvolvimento configurado para uma das linguagens anteriores:

JavaScript (para navegadores) e GDScript fornecem suporte para conteúdo multimídia.

Também estou considerando melhorar os editores online para programação dentro do website, em um estilo de curso interativo. Atualmente, a página de ferramentas fornece opções para:

Os editores possuem suporte para imagens em navegador. Contudo, Python e Lua utilizam a sintaxe para JavaScript com canvas. Caso eu criasse uma abstração e adicionasse suporte para áudio, eles tornar-se-iam opções válidas (ao menos para primeiras atividades).

Contudo, convém configurar um Ambiente Integrado de Desenvolvimento (em inglês, Integrated Development Environment ou IDE), ou a combinação de editor de texto com interpretador assim que possível para programar em sua máquina com maior eficiência. Ambientes online são práticos, mas ambientes locais são mais potencialmente mais completos, eficientes, rápidos e personalizáveis. Caso queira tornar-se profissional, você precisará de um ambiente de desenvolvimento configurado. Quanto antes o fizer, melhor.

Versão em Vídeo

Este tópico possui versões compactas em vídeo no canal do autor no YouTube:

Contudo, esta versão em texto é mais completa.

Documentação

Pratique consultar pela documentação:

Praticamente todos os links possuem um campo para buscas. Em Lua, a documentação está em uma única página. Você pode procurar por entradas usando o atalho Ctrl F (busca ou search).

Cara ou Coroa?

O tópico anterior (Aleatoriedade e Ruído) de Ideias, Regras, Simulação introduziu o uso de números aleatórios (tecnicamente, pseudoaleatórios) para auxiliar a criação de simulações estocásticas.

Lançamentos de moedas e dados são dois dos exemplos mais simples de simulações com aleatoriedade. A implementação necessária pode ser descrita em poucas frases:

  1. Para uma moeda, pode-se sortear um número real aleatório entre 0.0 e 1.0, dividir o intervalo em 0.5, e designar o intervalo 0.0 a 0.5 (não inclusive) para cara, e 0.5 (inclusive) a 1.0 (não-inclusive) para coroa. Seria igualmente válido inverter os intervalos para cara e coroa.

    Outra alternativa seria sortear um valor aleatório inteiro, usar o resto da divisão por 2, e designar resto 0 para cara e 1 para coroar (ou vice-versa). Para tornar o código mais fácil de ler, poder-se-ia definir uma constante HEAD (cara) com valor 0, e uma constante TAIL (coroa) com valor 1. Embora o uso de termos no plural (heads e tails) seja mais comum em língua inglesa, este tópico usará os termos no singular.

  2. Para um dado honesto (isto é, com probabilidades iguais para cada possível resultado) de 6 faces, basta sortear um número aleatório inteiro, usar o operador de um resto de divisão por 6, e somar um ao resultado. O resultado seria um número entre 1 e 6.

Ainda mais simples, as operações podem ser feitas trivialmente usando a função random_integer() definida no tópico anterior. Para um dado honesto de seis faces, bastaria fazer uma chamada como random_integer(1, 6). Analogamente, random_integer(0, 1) seria uma opção para uma moeda.

Como a parte das regras para simulação é simples, o restante desta seção foca em estruturas condicionais, a criação e uso de funções e de procedimentos, estruturas de repetição, e refatorações. O intuito é explicá-las para pessoas que não seguiram o material de Aprenda Programação.

Caso você saiba usá-las, você pode avançar para a próxima seção para criar gráficos para a saída.

Contudo, é interessante notar que, muitas vezes, é possível criar protótipos de simulações sem o uso de gráficos. Em alguns casos, é mais simples e rápido testar uma idéia em um programa para console (terminal) que começar com gráficos. Quando a implementação do protótipo estiver satisfatória, pode-se começar a criar os gráficos para uma melhor apresentação dos resultados.

Variáveis e Atribuições

Para uma introdução gentil, a explicação começará do uso de variáveis.

Como a simulação é simples, bastará uma variável para armazenar o número aleatório. Ela pode chamar resultado, ou result.

Para o sorteiro do número aleatório, deve-se gerar uma semente para que os resultados sejam diferentes a cada sessão de uso. Em seguida, basta usar a função para geração de um número pseudoaleatório para sorteio do número.

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)

Em Python e Lua, random.randint() e math.random() permitem escolher o intervalo para os números. Em JavaScript, a implementação de random_integer() também permite. Porém, randi() não permite em GDScript. Para um exemplo aplicável a qualquer linguagem de programação, poder-se-ia modificar o intervalo para algo como 1 a 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)

Assim, o número gerado tornou-se grande em todas as implementações. Ou seja, será necessário convertê-lo para dois valores para a representação de cara ou coroa.

Novamente, a escolha foi feita somente para fins didáticos. Por exemplo, bastaria usar math.random(0, 1) diretamente em Lua.

Operações Aritméticas

Computadores operam sobre quatro tipos de dados principais:

  1. Números inteiros;
  2. Números reais, normalmente em representação de ponto flutuante, chamada de float;
  3. Valores lógicos, isto é, Verdadeiro (True) ou Falso (False);
  4. Cadeias de caracteres.

Em um nível mais baixo, todos os tipos são codificados como números binários (consulte Aprenda Programação: Variáveis e Constantes). Em um nível ainda mais baixo, todo valor é uma combinação de presença ou ausência de eletricidade (ou magnetismo, ou outra tecnologia usada para a memória) em circuitos.

Em outras palavras, não existirá cara nem coroa para o computador. Apenas existirá uma abstração, que utilizará o número aleatório sorteado.

Assim, precisamos reduzir o valor de result para dois valores. Podemos fazer isso usando uma operação aritmética chamada de o resto de divisão (módulo). Para dois valores, pode-se efetuar um resto de divisão por 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)

Na nova versão, o resultado sempre será 0 ou 1. Ou seja, dois valores, como necessário para cara e coroa.

Pensamento Computacional: Abstração

Novamente, um computador não sabe o que é cara, nem coroa. Por outro lado, ele pode processar um número inteiro.

O objetivo, agora, é associar um código que mapeie os valores 0 e 1 para cara e coroa. Para o computador, result continuará sendo 0 ou 1. Para uma pessoa usando o programa, o intuito é escrever cara ou coroa mediante a associação estabelecida no programa.

Estruturas Condicionais

Simular o lançamento de uma moeda é uma forma simples de entender o uso de uma estrutura se/então/senão. Um exemplo em Lua pode ser ilustrativo. O emoji provavelmente não funcionará em muitos interpretadores de linha de comando; ele serve apenas para marcar o local em que o código de desenho será escrito em momento oportuno.

extends Control

func _ready():
    randomize()

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

    print("Fim")
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("Cara")
} else {
    console.log("$1 👑")
    console.log("Coroa")
}

console.log("Fim")
import random

random.seed()

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

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

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

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

print("Fim")

No exemplo, print(result) (ou console.log(result)) apenas serve o propósito de exibir o número aleatório que foi obtido. A linha será removida nos próximos exemplos. Da mesma forma, print("Fim") (ou console.log("Fim")) apenas ilustra que o programa retorna ao fluxo normal após terminal a estrutura de repetição. A linha também poderia ser removida.

Para uma visualização de código, pode-se considerar o fluxograma a seguir, criado em Flowgorithm (usado em Aprenda Programação). Para conveniência do autor, o fluxograma apresenta texto em Português e Inglês (para uso das duas traduções deste tópico). No fluxograma, pode-se notar que a estrutura condicional (representada pelo losango) define dois fluxos de execução mutuamente exclusivos. Ou seja, a execução de um inibe a execução do outro.

Representação do código de estrutura condicional em Lua como um fluxograma em Flowgorithm.

Outra boa forma de visualizar a execução do programa consiste em usar um depurador. Você pode aprender como em Aprenda Programação: Testes e Depuração. A versão em vídeo ilustra o uso na prática.

Para explicar o código, pode-se considerar a versão em Lua. A variável result armazena o resultado do sorteio de um número inteiro aleatório. Se result for igual a 0, o programa executa as linha 7 e 8, e ignora as linhas 10 e 11. Ou seja, o processo executa apenas a parte (bloco de código) em que a condição é verdadeira (then-part). Em seguida, o programa executa a linha 14, e termina.

Caso contrário, o programa executa as linhas 10 e 11, e ignora as linha 7 e 8. Ou seja, o processo executa apenas a parte em que a condição é falsa (else-part). Em seguida, o programa executa a linha 14, e termina.

Caso se deseje, usar o resultado do sorteio diretamente como parte da condição (ao invés de armazená-lo na variável result).

extends Control

func _ready():
    randomize()

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

    print("Fim")
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("Cara")
} else {
    console.log("$1 👑")
    console.log("Coroa")
}

console.log("Fim")
import random

random.seed()

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

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

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

print("Fim")

A nova versão é equivalente à anterior. Ela pode ser usada quando não houver a necessidade de usar um resultado intermediário (ou temporário) como parte da implementação.

Contudo, caso se precise armazenar o valor ou usá-lo várias vezes (em outras palavras, duas ou mais vezes), a variável será necessária. Em Lua com LÖVE, isso será necessário para preservar o valor do número aleatório. Para a versão atual em console (terminal), isso não é necessário. Por outro lado, caso se quisesse escrever o resultado como anteriormente, o uso da variável seria necessário. Afinal, chamar novamente a função de geração de número pseudoaleatório poderia gerar valores diferentes a cada chamada.

Caso existissem mais de dois possíveis resultados de interesse, poder-se-ia usar várias estruturas condicionais alinhadas para tratar cada um dos casos. Isso pode ser feito combinando um elseif em Lua, elif em Python e GDScript, e else if em JavaScript (JavaScript não possui uma versão especializada, então usa-se uma nova estrutura convencional). Mais tarde, esse tipo de construção será usado para desenhar faces de um dado. A próxima subseção explicará o uso.

Tratando Mais de Dois Casos

No caso de uma moeda real, os resultados podem ser apenas cara ou coroa. Por outro lado, em uma simulação, você poderia pensar em outros cenários.

Afinal, a simulação é sua. Você é quem faz a magia. Ou seja, você é quem dita as regras da simulação. Suas ideas, suas regras, suas simulações.

Por exemplo, talvez em 20% dos casos a moeda pudesse flutuar no ar. Nesse novo cenário, a chance de ocorrência de cara seria 40%, assim como a de coroa. Pode-se sortear números de 1 a 100 (ou 0 a 99, usando resto da divisão por 100) para gerar as porcentagens. Nesse caso em particular, também seria possível usar valores de 1 a 10 (ou 0 até 9 para resto de divisão por 10). Ou mesmo por 5.

Em seguida, pode-se definir condições usando-se operações relacionais e comparações.

extends Control

func _ready():
    randomize()

    var result = randi() % 10
    print(result)
    if (result < 4):
        print(":) 🙂")
        print("Cara")
    else:
        if (result < 8):
            print("$1 👑")
            print("Coroa")
        else:
            print("Moeda parou no ar...")

    print("Fim")
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("Cara")
} else {
    if (result < 8) {
        console.log("$1 👑")
        console.log("Coroa")
    } else {
         console.log("Moeda parou no ar...")
    }
}

console.log("Fim")
import random

random.seed()

result = random.randint(1, 10000) % 10
print(result)
if (result < 4):
    print(":) 🙂")
    print("Cara")
else:
    if (result < 8):
        print("$1 👑")
        print("Coroa")
    else:
        print("Moeda parou no ar...")

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

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

if (result < 4) then
    print(":) 🙂")
    print("Cara")
else
    if (result < 8) then
        print("$1 👑")
        print("Coroa")
    else
        print("Moeda parou no ar...")
    end
end

print("Fim")

É importante notar que, na nova versão, é obrigatório armazenar o valor aleatório em uma variável, já que ele é usado em duas comparações. Como os casos são mutuamente exclusivos (devido ao uso de else), o segundo if aninhado tem condição (result >= 4) and (result < 8), pois já se sabe que o número não é menor que 4. Da mesma forma, o último else tem condição (result >= 8), pois o valor não pode ser menor que 8 nesse ponto. O and é um operador lógico.

Assim, a primeira verificação trata os valores 0, 1, 2 e 3 para cara (ou seja, os 40% para cara). A segunda parte trata dos valores 4, 5, 6 e 7 para coroa (os 40% para coroa). Por fim, a última parta trata dos valores 8 e 9 para a moeda flutuando no ar (os 20% restantes). Como o resto da divisão por 10 gera números de 0 a 9, as estruturas condicionais criadas tratam todos os casos como propostos.

Como é comum definir casos mutuamente exclusivos, muitas linguagens de programação fornecem uma palavra reservada como elif ou elseif (ou else if, caso não exista) para simplificar a construção do código.

extends Control

func _ready():
    randomize()

    var result = randi() % 10
    print(result)
    if (result < 4):
        print(":) 🙂")
        print("Cara")
    elif (result < 8):
        print("$1 👑")
        print("Coroa")
    else:
        print("Moeda parou no ar...")

    print("Fim")
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("Cara")
} else if (result < 8) {
    console.log("$1 👑")
    console.log("Coroa")
} else {
    console.log("Moeda parou no ar...")
}

console.log("Fim")
import random

random.seed()

result = random.randint(1, 10000) % 10
print(result)
if (result < 4):
    print(":) 🙂")
    print("Cara")
elif (result < 8):
    print("$1 👑")
    print("Coroa")
else:
    print("Moeda parou no ar...")

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

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

if (result < 4) then
    print(":) 🙂")
    print("Cara")
elseif (result < 8) then
    print("$1 👑")
    print("Coroa")
else
    print("Moeda parou no ar...")
end

print("Fim")

Para mais informações, pode-se consultar Aprenda Programação: Estruturas de Condição.

A nova versão da implementação é equivalente à primeira. Se o resultado não é cara, nem coroa, então a moeda parou no ar. Simples assim; não é necessário explicar as causas físicas. Simulações podem ser metafísicas, porque elas originam de sua imaginação. Ou seja, de suas ideas.

O restante deste tópico assume a existência de gravidade. Ou seja, o resultado sempre será cara ou coroa.

Constantes

Para tornar o código mais legível, pode-se definir constantes.

extends Control


const HEAD = 0
const TAIL = 1


func _ready():
    randomize()

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

    print("Fim")
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("Cara")
} else {
    console.log("$1 👑")
    console.log("Coroa")
}

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


HEAD: Final = 0
TAIL: Final = 1


random.seed()

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

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

math.randomseed(os.time())

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

print("Fim")

A próxima imagem apresenta uma implementação similar em Flowgorithm, para visualização. Como Flowgorithm não permite a declaração de constantes, usou-se variáveis. Na imagem, os valores para as constantes HEAD e TAIL estão invertidos; como os valores são simbólicos, inversão não afeta a corretude do programa.

Representação do código com constante em Lua como um fluxograma em Flowgorithm.

Desde Lua 5.4, também é possível definir uma constante real usando-se <const>.

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

math.randomseed(os.time())

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

print("Fim")

Como ZeroBrane Studio não inclui a versão 5.4 do interpretador (quando da escrita deste artigo), o código anterior apresentará erro sintático.

Subrotinas: Funções e Procedimentos

Para simplificar ainda mais a leitura do código e escrever um bloco que pode ser reusado várias vezes, pode-se definir uma subrotina, como um procedimento ou uma função. Funções retornam resultados; procedimentos são usados para efeitos colaterais (como, por exemplo, alterações de memória, ou operações de entrada e saída -- como escrita de texto ou desenho na tela).

Por exemplo, o sorteio do número aleatório poderia ser implementado como uma função para lançamento de uma moeda chamada throw_coin(). Se você quisesse escolher outro nome (como lancar_moeda() ou cara_ou_coroa()), você também poderia.

extends Control


const HEAD = 0
const TAIL = 1


func throw_coin():
    return randi() % 2


func _ready():
    randomize()

    if (throw_coin() == HEAD):
        print(":) 🙂")
        print("Cara")
    else:
        print("$1 👑")
        print("Coroa")

    print("Fim")
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("Cara")
} else {
    console.log("$1 👑")
    console.log("Coroa")
}

console.log("Fim")
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("Cara")
else:
    print("$1 👑")
    print("Coroa")

print("Fim")
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("Cara")
else
    print("$1 👑")
    print("Coroa")
end

print("Fim")

A próximas imagens apresentam uma implementação similar em Flowgorithm, para visualização.

Representação do código com a definição da função throw_coin() em Lua como um fluxograma em Flowgorithm.
Representação do código do programa principal em Lua como um fluxograma em Flowgorithm.

Agora é possível chamar throw_coin() sempre que se quiser lançar uma moeda.

Funções e procedimentos podem receber parâmetros (argumentos). Para ilustrar a criação e uso de um procedimento com argumento, poder-se-ia definir um procedimento print_coin() para escrever a face do resultado a partir de um parâmetro com o valor da face.

extends Control


const HEAD = 0
const TAIL = 1


func throw_coin():
    return randi() % 2


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


func _ready():
    randomize()

    print_coin(throw_coin())

    print("Fim")
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("Cara")
    } else {
        console.log("$1 👑")
        console.log("Coroa")
    }
}

print_coin(throw_coin())

console.log("Fim")
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("Cara")
    else:
        print("$1 👑")
        print("Coroa")


random.seed()

print_coin(throw_coin())

print("Fim")
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("Cara")
    else
        print("$1 👑")
        print("Coroa")
    end
end


math.randomseed(os.time())

print_coin(throw_coin())
print("Fim")

A próximas imagens apresentam uma implementação similar em Flowgorithm, para visualização. A função throw_coin() possui o mesmo fluxograma definido anteriormente.

Representação do código com a definição do procedimento print_coin() em Lua como um fluxograma em Flowgorithm.
Representação do código do programa principal em Lua como um fluxograma em Flowgorithm.

Com a mudança, é simples entender o que o programa faz pela leitura de print_coin(throw_coin()). Assumindo que os nomes das subrotinas sejam condizentes com as implementações, o programa imprime o resultado de um lançamento de moeda.

Muitas linguagens de programação definem uma função main() como ponto de entrada (como definido em Aprenda Programação: Ponto de Entrada e Estrutura de Programa) para o programa. O ponto de entrada é o local em que o programa começa. Lua não define (o programa começa na primeira linha de código do script fornecido ao interpretador), mas seria possível modificar o código para criar uma.

extends Control


const HEAD = 0
const TAIL = 1


func throw_coin():
    return randi() % 2


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


func _ready():
    randomize()

    print_coin(throw_coin())

    print("Fim")
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("Cara")
    } else {
        console.log("$1 👑")
        console.log("Coroa")
    }
}


function main() {
    print_coin(throw_coin())

    console.log("Fim")
}


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("Cara")
    else:
        print("$1 👑")
        print("Coroa")


def main():
    random.seed()

    print_coin(throw_coin())

    print("Fim")


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("Cara")
    else
        print("$1 👑")
        print("Coroa")
    end
end


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

    print_coin(throw_coin())
    print("Fim")
end


main()

Como a implementação de main() do exemplo não retorna um valor, tecnicamente ela seria um procedimento ao invés de uma função. Em muitas linguagens de programação, main() retorna um valor inteiro usado como código para informar se o processo terminou com sucesso (ou se falhou). Isso é útil para a criação de shell scripts. Para aprender a criar programas para linha de comando, você pode consultar Aprenda Programação: Entrada em Linha de Comando.

Subrotinas para Moedas e Dados em GDScript, JavaScript, Python e Lua

Pode-se realizar os passos anteriores para qualquer linguagem de programação moderna. O próximo exemplo implementa throw_coin(), throw_dice() e print_coin() em GDScript, JavaScript, Python e Lua, usando random_integer() como definida anteriormente.

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("Cara")
    else:
        print("Coroa")


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("Cara")
    } else {
        console.log("Coroa")
    }
}

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("Cara")
    else:
        print("Coroa")


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("Cara")
    else
        print("Coroa")
    end
end


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

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


main()

Nas implementações, throw_dice() sorteia um valor entre 1 a 6 para representar a face resultante.

Subrotinas: Parâmetros com Valor Padrão

Para um dado honesto mais sofisticado, o número de faces poderia ser um parâmetro. Assim, poder-se-ia sortear um valor entre 1 e o número de faces desejado.

Em particular, também é possível definir um valor padrão para um parâmetro, como comentado em Aprenda Programação: Registros. Caso se omita o parâmetro na chamada, a implementação utilizaria o valor padrão definido. Por exemplo, throw_dice() poderia usar 6 como valor padrão. Por outro lado, uma chamada com um parâmetro como throw_dice(20) sortearia um valor entre 1 e 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("Cara")
    else:
        print("Coroa")


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("Cara")
    } else {
        console.log("Coroa")
    }
}


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("Cara")
    else:
        print("Coroa")


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("Cara")
    else
        print("Coroa")
    end
end


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

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


main()

Como Lua não permite definir um valor padrão na assinatura da subrotina, o uso de or permite alterar o valor caso ele seja omitido na chamada. O valor recebido seria nil; como nil em um or resulta em false, a expressão receberá o valor definido no início da implementação (caso nenhum outro valor seja fornecido).

Laços ou Loops: Lançando Moedas e Dados Milhares de Vezes

Por fim, para lançar moedas e dados milhares de vezes, bastaria usar Estruturas de Repetição (ou Laços ou Loops). Em particular, poder-se-ia usar um acumulador para contar resultados de interesse.

Os próximos exemplos contam o número de ocorrências de caras e coroas em 10000 lançamentos de uma moeda.

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("Cara")
    else:
        print("Coroa")


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("Caras: " + str(heads_count))
    print("Coroas: " + 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("Cara")
    } else {
        console.log("Coroa")
    }
}


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("Caras: " + heads_count)
    console.log("Coroas: " + 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("Cara")
    else:
        print("Coroa")


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("Caras: " + str(heads_count))
    print("Coroas: " + 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("Cara")
    else
        print("Coroa")
    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("Caras: " .. heads_count)
    print("Coroas: " .. tails_count)
end


main()

A próxima imagem apresenta uma implementação similar em Flowgorithm, para visualização. Apenas o programa principal (main()) é mostrado. As subrotinas possuem os mesmos fluxogramas anteriores.

Representação do código com estrutura de repetição como um fluxograma em Flowgorithm.

Execute o código do projeto algumas vezes e verifique os resultados. Os valores de caras e coroas deverá ser próximo na maioria das execuções.

Ainda melhor, você pode adicionar uma estrutura de repetição para executar a simulação várias vezes. Os próximos blocos sugerem como atualizam o ponto de entrada para ilustrar a abordagem.

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("Caras: " + str(heads_count))
        print("Coroas: " + 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("Caras: " + heads_count)
        console.log("Coroas: " + 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("Caras: " + str(heads_count))
        print("Coroas: " + 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("Caras: " .. heads_count)
        print("Coroas: " .. tails_count)
        print()
    end
end

Repetir simulações centenas ou milhares de vezes será retomado futuramente Ideias, Regras, Simulação, para a criação de Simulações de Monte Carlo. Neste tópico, as repetições são apenas uma curiosidade para praticar o uso de estruturas de repetição.

Primitivas Gráficas: Quadrados e Retângulos

Para o desenho de uma moeda, poder-se-ia desenhar um círculo ou um arco com um número dentro, representando a face sorteada. Analogamente, o desenho de um dado poderia usar um quadrado com círculos ou um número dentro dele.

Além de Pontos, Linhas, e Arcos (que já sabemos desenhar), primitivas gráficas comumente incluem retângulos e quadrados. Antes de usá-las, convém criarmos as nossas próprias para entender como elas podem ser implementadas.

Canvas em HTML para JavaScript

Como na Introdução de Ideias, Regras, Simulação, JavaScript requer um arquivo HTML auxiliar para a declaração do canvas. O nome do arquivo HTML é de sua escolha (por exemplo, index.html). O exemplo a seguir assume que o arquivo JavaScript terá nome script.js e que o canvas terá identificador (id) canvas. Caso você altere os valores, lembre-se de modificá-los nos arquivos conforme necessário.

<!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;">
      >
          Acessibilidade: texto alternativo para conteúdo gráfico.
      </canvas>
    </div>

    <!-- NOTA Atualizar com nome do arquivo JavaScript. -->
    <script src="./script.js"></script>
  </body>
</html>

O arquivo também mantém o lembrete sobre acessibilidade. Neste tópico, o conteúdo tornar-se-á ainda mais inacessível para pessoas com certos tipos de deficiência visual, devido a adição de conteúdo gráfico sem opções alternativas para comunicação do conteúdo.

No futuro, a intenção é abordar formas de tornar simulações mais acessíveis. Neste momento, este lembrete é apenas informativo, para conscientizar você da importância de acessibilidade.

Desenhando Quadrados e Retângulos

Desenhar um retângulo ou um quadrado com linhas é simples quando já se sabe desenhar pixels: basta repetir o desenho de linhas em pixels adjacentes. Outra forma de entender a idéia consiste em pensar em uma linha como um retângulo no qual um das medidas (altura ou largura) seja igual a 1 pixel.

Assim, para desenhar um retângulo colorido, basta usar uma estrutura de repetição que desenhe uma linha horizontal para cada pixel da altura desejada. A implementação descrita está disponível em franco_draw_rectangle().

Para desenhar um quadrado, basta desenhar um retângulo com a altura igual a largura, como feito em franco_draw_square().

# Raíz deve ser um Node que permita desenhar usando _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

O resultado do desenho é exibido no próximo canvas.

Desenho de um retângulo e de dois quadrados, todos brancos, sobre fundo preto usando linhas.

Caso se desejasse desenhar apenas o contorno do retângulo, bastaria desenhar as quatro linhas que delimitam a forma.

Primitivas Gráficas para Quadrados e Retângulos

Como foi o caso em Pixels e Primitivas Gráficas (Pontos, Linhas, e Arcos), APIs gráficas comumente fornecem subrotinas para o desenho de retângulos. De fato:

  • Godot fornece draw_rect();
  • JavaScript com canvas fornece fillRect() (usado anteriormente para desenhar a cor de fundo);
  • Python com PyGame possui pygame.draw.rect();
  • Lua com LÖVE define love.graphics.rectangle().

Por outro lado, como um quadrado é um retângulo, nem toda API fornece uma subrotina para desenhar um quadrado. Assim, franco_draw_square() desenhará um quadrado usando a primitiva para o desenho de retângulos. Também seria possível manter a implementação anterior, usando franco_draw_rectangle() na implementação de franco_draw_square() para o desenho do quadrado.

# Raíz deve ser um Node que permita desenhar usando _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

O resultado do desenho é exibido no próximo canvas.

Desenho de um retângulo e de dois quadrados, todos brancos, sobre fundo preto usando primitivas gráficas.

Assim, o repertório atual de primitivas gráficas inclui pixels (pontos), linhas, arcos, retângulos e quadrados. Ou seja, temos tudo de que precisamos para este tópico.

Desenhando Moedas

Hora de usar seus talentos artísticos para desenhar o resultado do lançamento de uma moeda. É provável que suas habilidades artísticas sejam suficientes para fazer um desenho melhor que o do autor.

No próximo exemplo, o objetivo é desenhar o resultado do lançamento da moeda (ou seja, cara ou coroa) dentro de um arco. Para isso, basta combinar trechos de código de exemplos anteriores para simular o lançamento da moeda, e gerar um desenho para cara ou coroa. O desenho substituirá o texto e o emoji na console com gráficos simples em uma janela.

É importante sortear o valor apenas uma vez, para que o resultado não se altere em caso de redesenhos da janela. No código, o resultado do sorteio é armazenado em result.

A parte mais longa da implementação é o código para desenhar cada face moeda. O uso de valores em função (como proporção) de WIDTH (largura) e HEIGHT (altura) permitem redimensionar o desenho caso se altere o valor das constantes. Para melhorar o código, poder-se-ia atualizar os valores quando se altera o tamanho da janela manualmente quando o programa está em execução. Para uma versão mais simples, poder-se-ia manter um tamanho fixo para a janela e usar valores específicos.

# Raíz deve ser um Node que permita desenhar usando _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

Um possível resultado do desenho é exibido no próximo canvas.

Simulação de lançamento de moedas, com desenhos para resultados de cara e coroa.

O exemplo implementado para o canvas incorpora uma animação atualizada a cada segundo. Assim, caso você espere algum tempo, você deverá ver ambos os resultados. O tempo de transição de uma imagem para outra pode variar, já que se trata de resultados aleatórios para a moeda.

Em todas as versões, o desenho do círculo externo poderia ser movido para antes da estrutura condicional, dado que ele é feito para ambos os casos. Faça a mudança para constatar a afirmação.

A versão em Python com PyGame aproxima 2 * math.pi devido à limitação de pygame.gfxdraw.arc() comentada em Pontos, Linhas, e Arcos. Após introduzirmos uma subrotina para desenhos do círculos, poderemos usá-la ao invés de arcos.

Desenhando Dados

O desenho de um dado pode usar um quadrado e um retângulo para as bordas. Para o número sorteado, pode-se desenhar o valor usando-se arcos ou círculos, ou escrevê-lo como texto.

O próximo exemplo desenha o valor com arcos (formando círculos). O código desenha um número de círculos correspondeste ao valor sorteado. Por exemplo, um círculo para 1, três círculos para 3, e cinco círculos para 5. Valores ímpares sempre desenham um círculo na posição central da janela. Valores maiores que 1 distribuem círculos em duas (valores pares) ou três colunas (valores ímpares).

# Raíz deve ser um Node que permita desenhar usando _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

Um possível resultado do desenho é exibido no próximo canvas.

Simulação de lançamento de dados com seis faces, com desenhos de círculos para cada um dos possíveis resultados. O número de círculos desenhados corresponde ao valor sorteado.

O canvas sorteia valores a cada segundo para exibir diferentes resultados ao longo do tempo. Exceto em caso de um gerador com distribuição degenerada, ou caso se conheça a semente e o algoritmo para geração dos números, o próximo valor será uma surpresa. Ele estará entre 1 e 6, mas qual será?

A implementação feita é bastante simples. De fato, ela contém código duplicado, que poderia ser removido com uma solução mais elegante.

A solução também seria mais difícil de ler e entender. Para evitar introduzir complexidade desnecessária nestes primeiros tópicos de Ideias, Regras, Simulação, o autor manterá as linhas duplicadas no código do exemplo. Nem sempre a solução mais concisa é a mais legível.

Caso você queira praticar e remover linhas duplicadas, existem diferentes formas de modificar o código. Por exemplo, pode-se desenhar arcos em cada posição dependendo do valor sorteado, ou usar uma estrutura de repetição para incrementar os deslocamentos entre cada arco.

Mostrar/Ocultar Possível Solução em 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

Uma solução mais avançada poderia mapear os valores em uma matriz 3x3, desenhando valores marcados para na matriz. Essa seria mais simples de ler e entender, mas estruturas de dados serão um tópico futuro em Ideias, Regras, Simulação. Se você já estudou Aprenda Programação: Vetores (Arrays), Cadeias de Caracteres (Strings), Coleções (Collections) e Estruturas de Dados, você pode tentar implementá-la.

Conceitos Abordados de Aprenda Programação

Conceitos de Aprenda Programação abordados neste tópico:

  1. Ponto de entrada;
  2. Saída;
  3. Tipos de dados;
  4. Variáveis e constantes;
  5. Aritmética;
  6. Operações relacionais e comparações;
  7. Operações lógicas;
  8. Estruturas de condição;
  9. Subrotinas (funções e procedimentos);
  10. Estruturas de repetição (laços ou loops);
  11. Bibliotecas.

É interessante notar que mesmo primitivas para desenhos podem utilizar quase todos os conceitos básicos de programação.

Novamente, os valores para cores de pixels em Lua usaram tabelas (dicionários) como vetores, descritos em Coleções.

Novos Itens para Seu Inventário

Habilidades de Pensamento Computacional:

Ferramentas:

  • Lista de emojis.

Habilidades:

  • Prototipação de simulações usando uma versão para console (terminal);
  • Desenho de retângulo usando estruturas de repetição;
  • Desenho de retângulo usando primitivas gráficas;
  • Desenho usando tamanhos proporcionais, por meio de porcentagens da largura e altura da janela.

Conceitos:

  • Primitivas gráficas: retângulo;
  • Primitivas gráficas: quadrado.

Recursos de programação:

  • Desenhos de retângulos e quadrados.

Pratique

Para aprender programação, prática deliberada deve acompanhar conceitos. Tente fazer os próximos exercícios para praticar.

  1. Modifique o exemplo do dado para usar 8 faces ao invés de 6. Atualize o código de desenho para ilustrar os novos resultados.

  2. Ao invés de desenhar os valores, escreva o valor como uma mensagem de texto dentro do círculo (moeda) ou do quadrado (dado).

    Dependendo da linguagem de programação, você precisará converter o número inteiro sorteado para uma cadeia de caracteres. Para aprender como fazer isso, consulte Aprenda Programação: Tipos de Dados.

    Para desenhar os resultados como texto, consulte Ideias, Regras, Simulação: Introdução (Janela e Olá, mundo!).

  3. Por que criar desenhos usando valores de altura e largura ao invés de tamanhos específicos? Quais as vantagens? Quais as desvantagens?

  4. Se a implementação dos exemplos utiliza tamanhos baseados em altura e largura da janela, por que o desenho não é atualizado automaticamente quando se redimensiona a janela com o programa em execução?

    Dica. O valor das constantes nunca é alterado. Para considerar novos valores, eles precisariam ser variáveis alteradas a cada mudança de tamanho da janela.

Aprofundamentos

Em Ideias, Regras, Simulação, esta seção apresenta conteúdo complementar.

Emojis

Um emoji é codificado como um caractere. Essa é a razão dele ter sido declarado no texto de uma cadeia de caracteres no exemplo de lançamento de moedas para console (terminal) usando Lua.

A codificação usada para texto com suporte a emoji é chamada de Unicode. Ela também é usada para codificação de texto com acentos, assim como outros caracteres especiais e letras de centenas de idiomas. Em computadores, texto é armazenado como números (mais especificamente, binários). Para aprender mais sobre como computadores armazenam texto, você pode consultar Aprenda Programação: Tipos de Dados.

Para descobrir o código de um emoji, pode-se acessar esta lista oficial.

Para criar um emoji acessível em HTML para conteúdo Web, como este 👑, é necessário adicionar uma tag especial com uma descrição textual.

<span role="img" aria-label="Emoji de uma coroa">👑</span>

Assim, pessoas que fazem uso de tecnologias como leitores de tela poderão entender o conteúdo. Isso é importante, por exemplo, para pessoas cegas.

Infelizmente, essa técnica não se aplica a outras linguagens de programação. Consideraremos accessibilidade em simulações em tópicos futuros de Ideias, Regras, Simulação.

Próximos Passos

Para programar bem, é fundamental dominar habilidades de pensamento computacional. O material de Aprenda Programação argumenta que programar é resolver problemas. Linguagens de programação são meras ferramentas para a implementar a sua solução.

Assim, embora este tópico pudesse ser bem mais curto, o autor optou por detalhar o processo de como escrever um programa de computador. O intuito é ajudar você a tornar-se mais independente e capaz de resolver problemas usando computadores. Afinal, o objetivo é permitir que você crie seus próprios sistemas, aplicativos (apps) e simulações no futuro.

Para a criação de uma simulação, convertermos uma ideia para código de computador implementando regras para processar dados. Isso é feito por meio de abstrações para decomposição de dados (por exemplo, usando variáveis ou registros) e decomposição funcional (por exemplo, via subrotinas como funções e procedimentos).

Desta vez, ao invés de criarmos um desenho caótico, utilizamos o resultado do sorteio de um número aleatório para ilustrar o resultado obtido. Abstraímos o sorteio de dois valores como uma moeda, e o sorteio de seis valores como um dado.

Contudo, os desenhos ficariam mais bonitos caso estivessem coloridos (ao invés de meros contornos). Para uma apresentação mais bonita, seria interessante preencher os contornos dos arcos com uma cor para realmente desenhar um círculo.

Ferramentas para desenho de imagens comumente contam como uma funcionalidade representada por um balde de tinta, que preenche uma região com uma cor. Podemos implementá-la como um algoritmo.

Assim, o próximo tópico introduz algumas primitivas gráficas adicionais -- desta vez, para o desenho de círculos, elipses e polígonos. Tanto como contornos, quanto com preenchimento.

No mais, se você criou uma ilustração criativa ou interessante, considere compartilhá-la. Alternativamente, se você considerou este material útil, você também pode compartilhá-lo. Se possível, use as hashtags #IdeiasRegrasSimulação e #FrancoGarciaCom.

Agradeço a atenção. Até a próxima!

Ideias, Regras, Simulação

  1. Motivação;
  2. Introdução: Janela e Olá Mundo;
  3. Pixels e primitivas gráficas (pontos, linhas, e arcos);
  4. Aleatoriedade e ruído;
  5. Moedas e dados, retângulos e quadrados;
  6. Desenhando com primitivas gráficas (contornos e preenchimento para círculos, elipses e polígonos);
  7. Salvando e carregando arquivos de imagens;
  8. ...

A escrita do material está em progresso; portanto, se você chegou muito cedo e os itens anteriores não possuem links, por favor, retorne a esta página para conferir as atualizações.

Caso queira entrar em contato ou tenha alguma dúvida, você pode conversar comigo das seguintes maneiras:

Contatos e redes sociais também estão no rodapé das páginas.

Suas opiniões sobre a série serão fundamentais para que eu possa tornar o material mais acessível e simples para mais pessoas.

  • Vídeo
  • Informática
  • Programação
  • Iniciante
  • Pensamento Computacional
  • Ideias, Regras, Simulação
  • Python
  • PyGame
  • Lua
  • LÖVE (Love2D)
  • Javascript
  • HTML Canvas
  • Godot
  • Gdscript
  • Flowgorithm