# GO: Desafio do Quiz (simplificado)

*Nota: esse artigo foi publicado primeiramente no blog.codeaspiras.dev, que foi encerrado. Transferi para esse blog para que o mesmo não se perdesse.*

Olá, leitores do CodeAspiras!

Quem participa do nosso canal no Discord sabe que, de tempos em tempos, nós lançamos alguns desafios de programação no canal #desafios. Alguns são bem simples, outros meio complexos. Eu vou destrinchar o que foi mais famoso até então por aqui pra disseminar conhecimento. <s>Se você gostar e quiser buscar mais desafios, é só chegar junto no Discord e ir fazendo o que estiver por lá</s>.

Ah, um detalhe antes de começar: esse é o desafio **simplificado**. O original exige um pouco mais de conhecimento, como leitura/escrita em arquivo, decodificar/encodar JSON, etc. Como ainda tem muito estudante no começo ainda, decidi retirar esses obstáculos e deixar o desafio mais simples, gerando o que verá a seguir.

<center><img src="https://media.giphy.com/media/75yYfqYy5tmHm/giphy.gif?cid=790b7611o0l9w2k7ecqlo50a7f1q6fr228v9sdf4gigaijhj&ep=v1_gifs_search&rid=giphy.gif&ct=g" /></center>

## O desafio

> Faça um programa que inicializa algumas variáveis de um quiz, faz as perguntas para colher as respostas e imprime o resultado.

Obs.: não está sendo determinado como seu programa deve funcionar!

### Impressão esperada no terminal

```bash
Pergunta 1: Essa seria a primeira pergunta...?
Opção 1: Sim...?
Opção 2: Não....?
Resposta: 1
--
Pergunta 2: Essa seria a terceira pergunta...?
Opção 1: Sim...?
Opção 2: Não....?
Resposta: 1
--
Resultado: você acertou 1 de 2 perguntas!!
```

E é isso, não tem mais nenhuma descrição. A ideia é que seu programa inicie já imprimindo:

```bash
Pergunta 1: Essa seria a primeira pergunta...?
Opção 1: Sim...?
Opção 2: Não....?
Resposta:
```

E aguarde o usuário responder. Daí o usuário responde `1`, que é a resposta correta.

Então seu programa continuará o processo, imprimindo uma separação e a próxima pergunta:

```bash
--
Pergunta 2: Essa seria a terceira pergunta...?
Opção 1: Sim...?
Opção 2: Não....?
Resposta:
```

E aí, mais uma vez, irá aguardar o usuário responder. Respondendo `1`, que é a resposta errada, o programa deverá então finalizar e imprimir uma separação e o resultado, informando que acertei apenas uma das duas perguntas:

```bash
--
Resultado: você acertou 1 de 2 perguntas!!
```

As únicas entradas do usuário são as respostas, que são números.

Sacou? Quer tentar desenvolver uma solução aí antes de continuar lendo?

Se for tentar, aprecie esse gif e só role a tela quando terminar!

<center><img src="https://media.giphy.com/media/MZiuuEnG0KOvdu2Grw/giphy.gif?cid=ecf05e47rt7g50bibf5xejrd1z2o2yilnttkc2lsnkytl0ek&ep=v1_gifs_search&rid=giphy.gif&ct=g" /></center>

## A solução (em Golang)

A maior dificuldade que as pessoas tem ao tentar solucionar desafios como esse é dar o primeiro passo. É como se você estivesse dirigindo um carro bem velho, dando partida no meio de uma ladeira. Se você tentou pensar em alguma coisa e não conseguiu, espero que possa absorver um pouco da técnica que vou te passar agora, que é infalível para construção de linhas de raciocínio.

Essa técnica se chama **Baby Steps**, que significa **Passos de Bebê,** em inglês. A ideia é que, ao invés de tentar montar, na sua cabeça, toda uma história de "começo, meio e fim", você deveria dar um passo de cada vez. Conforme você se torna experiente, será capaz de montar a história inteira na mente antes de colocar a mão na massa, mas enquanto não chega lá, não faz mal algum dar pequenos passos de bebê.

Nesse caso, eu já dei uma ajudinha ali em cima descrevendo que o desafio não está determinando como deve ficar seu código e exibindo, passo-a-passo, como as coisas devem aparecer no terminal.

> Nesse caso, você pode simplemente copiar tudo o que foi impresso no exemplo e... imprimir, Kiko?

Sim! Exatamente! Supondo que você ainda está aprendendo a fazer códigos em Golang, vamos por parte:

### 1 - Imprimindo todos os textos

```go
package main

import "fmt"

func main() {
    // Pergunta 1
    fmt.Println("Pergunta 1: Essa seria a primeira pergunta...?")
    fmt.Println("Opção 1: Sim...?")
    fmt.Println("Opção 2: Não....?")
    fmt.Print("Resposta: ")
    // ler resposta
    fmt.Println("") // retirar essa quebra de linha temporaria
    fmt.Println("--")

    // Pergunta 2
    fmt.Println("Pergunta 2: Essa seria a terceira pergunta...?")
    fmt.Println("Opção 1: Sim...?")
    fmt.Println("Opção 2: Não....?")
    fmt.Print("Resposta: ")
    // ler resposta
    fmt.Println("") // retirar essa quebra de linha temporaria
    fmt.Println("--")

    // Conclusão
    fmt.Println("Resultado: você acertou 1 de 2 perguntas!!")
}
```

Veja o código no Playground: [https://go.dev/play/p/HBrbJPnV\_SY](https://go.dev/play/p/HBrbJPnV_SY)

O código acima irá imprimir:

```bash
Pergunta 1: Essa seria a primeira pergunta...?
Opção 1: Sim...?
Opção 2: Não....?
Resposta: 
--
Pergunta 2: Essa seria a terceira pergunta...?
Opção 1: Sim...?
Opção 2: Não....?
Resposta: 
--
Resultado: você acertou 1 de 2 perguntas!!
```

Nesse momento, nós já estamos fazendo 90% do que foi pedido no desafio, concorda? Falta, literalmente, ler dois números. Caso não saiba como ler a entrada do terminal, basta pesquisar `<linguagem> scanf` e você vai achar alguns bons artigos explicando como fazer. Note que é importante ler mais do que um site pra tirar alguma conclusão, hein?

### 2 - Lendo respostas

No caso do nosso desafio, eu poderia usar o pacote `bufio`, que nos permite ter mais controle sobre qual stream de entrada estamos utilizando e tal, mas, para simplificar, quero utilizar a função `Scanln` do pacote `fmt`. É similar ao `scanf` de algumas outras linguagens por aí. Quer ver?

*PS.:****daqui para baixo****\*, para rodar o programa, precisará criar o arquivo localmente e rodando na mão. Pra fazer isso,\** [*baixe o Golang*](https://go.dev/doc/install)*, instale, depois crie uma pasta em qualquer lugar, abra essa pasta pelo terminal e digite*`go mod init quizsimp`*. Daí crie o arquivo*`main.go`*e cole o código nesse arquivo. Para executar, rode:*`go run .`

```go
package main

import "fmt"

func main() {
    var resposta string

    // Pergunta 1
    fmt.Println("Pergunta 1: Essa seria a primeira pergunta...?")
    fmt.Println("Opção 1: Sim...?")
    fmt.Println("Opção 2: Não....?")

    // Resposta 1
    fmt.Print("Resposta: ")
    if _, err := fmt.Scanln(&resposta); err != nil {
        fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
        return
    }

    fmt.Println("--")

    // Pergunta 2
    fmt.Println("Pergunta 2: Essa seria a terceira pergunta...?")
    fmt.Println("Opção 1: Sim...?")
    fmt.Println("Opção 2: Não....?")

    // Resposta 2
    fmt.Print("Resposta: ")
    if _, err := fmt.Scanln(&resposta); err != nil {
        fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
        return
    }

    fmt.Println("--")

    // Conclusão
    fmt.Println("Resultado: você acertou 1 de 2 perguntas!!")
}
```

### 3 - Contabilizando acertos

Agora você está armazenando a resposta do usuário na nova variável `resposta`, certo? Só que não estamos fazendo nada com isso!! Que tal adicionarmos uma outra variável `acertos` que contabiliza quantas respostas foram certas?

Além disso, podemos mudar a impressão de resultados de `Println` para `Printf` e formatar o texto, injetando o número de acertos:

```go
package main

import "fmt"

func main() {
    var resposta string
    acertos := 0

    // Pergunta 1
    fmt.Println("Pergunta 1: Essa seria a primeira pergunta...?")
    fmt.Println("Opção 1: Sim...?")
    fmt.Println("Opção 2: Não....?")

    // Resposta 1
    fmt.Print("Resposta: ")
    if _, err := fmt.Scanln(&resposta); err != nil {
        fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
        return
    }

    if resposta == "1" {
        acertos++
    }

    fmt.Println("--")

    // Pergunta 2
    fmt.Println("Pergunta 2: Essa seria a terceira pergunta...?")
    fmt.Println("Opção 1: Sim...?")
    fmt.Println("Opção 2: Não....?")

    // Resposta 2
    fmt.Print("Resposta: ")
    if _, err := fmt.Scanln(&resposta); err != nil {
        fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
        return
    }

    if resposta == "2" {
        acertos++
    }

    fmt.Println("--")

    // Conclusão
    fmt.Printf("Resultado: você acertou %d de 2 perguntas!!\n", acertos)
}
```

E pronto! Solucionamos o desafio............... Certo?

<center><img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbWZqc2FoNDh5cjRzaDByZmtob29nb25xdGpodjZlZjJyMTh4MGswbCZlcD12MV9naWZzX3NlYXJjaCZjdD1n/406ZMCwmTzYfo4E9cS/giphy.gif" /></center>

A verdade é que estamos gerando a mesma saída esperada que coloquei no começo desse desafio. Então, **sim, solucionamos**, porém não está nada elegante. Se quisermos fazer um Quiz com 100 perguntas, esse código ficaria gigantesco!

Nós podemos separar um pouco as responsabilidades aqui: que tal agruparmos todos os dados de cada pergunta em uma estrutura, deixando, então, o trecho de impressão/leitura fica responsável apenas por isso mesmo, simplificando os deveres?

### 4 - Organizando dados em estruturas

> E o que teria nessa estrutura de pergunta, Kiko?

Bem... A pergunta em si, as opções e a resposta esperada (vulgo gabarito). Seria algo assim:

```go
type QuizItem struct {
    Question string
    Options map[string]string
    RightOptionIndex string
}
```

E poderíamos organizar a primeira pergunta assim:

```go
QuizItem{
    Question: "Essa seria a primeira pergunta...?",
    Options: map[string]string{
        "1": "Sim...?",
        "2": "Não....?",
    },
    RightOptionIndex: "1",
}
```

E, com isso, teríamos quase todas as informações para imprimir tudo, concorda?

> Como assim, quase todas, Kiko?

É que falta sabermos qual é o número dessa pergunta. Como a ideia é termos uma lista de perguntas, teremos essa informação de outro lugar. Enfim, vamos transformar o código:

```go
package main

import "fmt"

type QuizItem struct {
	Question         string
	Options          map[string]string
	RightOptionIndex string
}

func main() {
	// prepara o quiz
	quiz := []QuizItem{
		{
			Question: "Essa seria a primeira pergunta...?",
			Options: map[string]string{
				"1": "Sim...?",
				"2": "Não....?",
			},
			RightOptionIndex: "1",
		},
		{
			Question: "Essa seria a terceira pergunta...?",
			Options: map[string]string{
				"1": "Sim...?",
				"2": "Não....?",
			},
			RightOptionIndex: "2",
		},
	}

	// executa o quiz
	acertos := 0
	for index, item := range quiz {
        // número da pergunta vem do index+1 !
		fmt.Printf("Pergunta %d: %s\n", index+1, item.Question)

		for num, opt := range item.Options {
			fmt.Printf("Opção %s: %s\n", num, opt)
		}

		fmt.Print("Resposta: ")
		var resposta string
		if _, err := fmt.Scanln(&resposta); err != nil {
			fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
			return
		}

		if resposta == item.RightOptionIndex {
			acertos++
		}

		fmt.Println("--")
	}

	// Conclusão
	fmt.Printf("Resultado: você acertou %d de 2 perguntas!!\n", acertos)
}
```

<center><img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExYjdqcGhuMG53Y2FzcXIyMXA5aXRyNmF2ZG1rMnl5bGt0cG1xZ2s2MiZlcD12MV9naWZzX3NlYXJjaCZjdD1n/LoIsP3fz02IjOUTc6t/giphy.gif" /></center>

E podemos ir ainda além!! E se quisermos exibir o resultado em forma de gabarito? Bem, só faz sentido se você puder distinguir as opções corretas das erradas, concorda? E pra isso você pode usar símbolos e tal, mas eu prefiro **COLORIR**.

### 5 - Exibindo gabarito após resultado

> Oi?! Dá pra colorir texto no terminal, Kiko?

Sim, dá! E para isso é só usarmos um comando que altera as propriedades de texto do terminal. Eu não sei exatamente qual é o nome disso, até tentei pesquisar mas parece que cada pessoa chama do jeito que quer, então não me ajudou muito, rs. Então vamos chamar do nosso jeito também! E deixo isso aberto para que alguém me corrija publicamente a vontade, hehe.

Eu chamo esse comando de **Editor de Fonte**. Ele funciona como uma tag HTML, tendo um comando no início de onde você quer trocar a fonte e outro no final. Em algumas linguagens, você pode acessar esse escape pela inicial `\e`, mas em Golang nós acessamos pela inicial `\033`:

* Começa com: `\033[<nums>m`
    
* Termina com: `\033[0m`
    

Nesse caso, a ideia é que, no lugar de `<nums>`, você coloque os números que vão definir o estilo de fonte a utilizar no trecho seguinte. Depois que acabar de imprimir o texto no estilo desejado, você reseta o estilo novamente com `\033[0m`. Sim, `0` é um dos possíveis códigos a se colocar no lugar de `<nums>`. E quando utilizamos mais de um código, nós separamos eles com ponto-e-vírgula (;).

Eu não vou colocar nenhuma tabela dos possíveis estilos aqui, mas o que nos interessa no momento:

* cor vermelha: `31`;
    
* cor verde: `32`.
    

Então, se você quiser imprimir uma linha vermelha, é só imprimir:

```go
fmt.Println("\033[31mEsse é o estilo com a cor vermelha\033[0m")
```

Dito isso, podemos fazer três constantes com as cores e uma função para fazer essa impressão formatada, o que acha?

```go
const (
    COLOR_RED = "31"
    COLOR_GREEN = "32"
)

func printStyled(text string, styles ...string) {
    fmt.Println("\033[" + strings.Join(styles, ";") + "m" + text + "\033[0m")
}
```

Assim, é só chamar:

```go
printStyled("Opção 1: ...", COLOR_GREEN) // para opções corretas
printStyled("Opção 2: ...", COLOR_RED) // para opções marcadas incorretamente
```

Mas ainda não estamos prontos, pois... Não salvamos nenhuma resposta do usuário.

Então vamos adicionar uma nova informação no `QuizItem`: `Answer string`, além de mudar o slice `[]QuizItem` para `[]*QuizItem` para usarmos sempre as mesmas referências, alterando e salvando as respostas na própria estrutura:

```go
package main

import (
	"fmt"
)

type QuizItem struct {
	Question         string
	Options          map[string]string
	RightOptionIndex string
    Answer           string
}

func main() {
	// prepara o quiz
	quiz := []*QuizItem{
		{
			Question: "Essa seria a primeira pergunta...?",
			Options: map[string]string{
				"1": "Sim...?",
				"2": "Não....?",
			},
			RightOptionIndex: "1",
		},
		{
			Question: "Essa seria a terceira pergunta...?",
			Options: map[string]string{
				"1": "Sim...?",
				"2": "Não....?",
			},
			RightOptionIndex: "2",
		},
	}

	// executa o quiz
	acertos := 0
	for index, item := range quiz {
		fmt.Printf("Pergunta %d: %s\n", index+1, item.Question)

		for num, opt := range item.Options {
			fmt.Printf("Opção %s: %s\n", num, opt)
		}

		fmt.Print("Resposta: ")
		if _, err := fmt.Scanln(&item.Answer); err != nil {
			fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
			return
		}

		if item.Answer == item.RightOptionIndex {
			acertos++
		}

		fmt.Println("--")
	}

	// Conclusão
	fmt.Printf("Resultado: você acertou %d de 2 perguntas!!\n", acertos)
}
```

E agora, podemos fazer um novo loop nas perguntas, colorindo as respostas corretas e erradas!!

```go
package main

import (
	"fmt"
	"strings"
)

const (
	COLOR_RED    = "31"
	COLOR_GREEN  = "32"
)

type QuizItem struct {
	Question         string
	Options          map[string]string
	RightOptionIndex string
	Answer           string
}

func printStyled(text string, styles ...string) {
	fmt.Print("\033[" + strings.Join(styles, ";") + "m" + text + "\033[0m")
}

func main() {
	// prepara o quiz
	quiz := []*QuizItem{
		{
			Question: "Essa seria a primeira pergunta...?",
			Options: map[string]string{
				"1": "Sim...?",
				"2": "Não....?",
			},
			RightOptionIndex: "1",
		},
		{
			Question: "Essa seria a terceira pergunta...?",
			Options: map[string]string{
				"1": "Sim...?",
				"2": "Não....?",
			},
			RightOptionIndex: "2",
		},
	}

	// executa o quiz
	acertos := 0
	for index, item := range quiz {
		fmt.Printf("Pergunta %d: %s\n", index+1, item.Question)

		for num, opt := range item.Options {
			fmt.Printf("Opção %s: %s\n", num, opt)
		}

		fmt.Print("Resposta: ")
		if _, err := fmt.Scanln(&item.Answer); err != nil {
			fmt.Printf("Um erro aconteceu ao ler a resposta: %s\n", err)
			return
		}

		if item.Answer == item.RightOptionIndex {
			acertos++
		}

		fmt.Println("--")
	}

	// gabarito
	fmt.Println("Gabarito:")
	for index, item := range quiz {
		fmt.Printf("Pergunta %d: %s\n", index+1, item.Question)

		for num, opt := range item.Options {
			label := fmt.Sprintf("Opção %s: %s", num, opt)
			if num == item.RightOptionIndex {
				printStyled(label, COLOR_GREEN)
			} else if num == item.Answer {
				printStyled(label, COLOR_RED)
			} else {
				fmt.Print(label)
			}

			fmt.Println("")
		}

		fmt.Println("--")
	}

    // Conclusão
	fmt.Printf("Resultado: você acertou %d de 2 perguntas!!\n", acertos)
}
```

![Print do resultado final do código final](https://cdn.hashnode.com/res/hashnode/image/upload/v1709125073516/e28e1356-1cec-4c08-88d7-bdea768b3806.png align="center")

## Conclusão

Nesse desafio, nós fizemos um código estritamente imperativo, mas poderíamos evoluir ainda mais. Por exemplo, eu fiz [minha versão do desafio com Bubbletea, sendo o desafio completo (lendo arquivos)](https://github.com/codeaspiras/goquiz). Enfim, essa é uma excelente atividade para iniciantes! Vocês aprendem a colocar em prática várias coisas de uma vez só. Além disso, você pode brincar com seus colegas fazendo perguntas reais nos programas finais! E se puder aceitar múltiplas respostas? E se for um Quiz de `scoring` ao invés de gabarito? No `scoring`, não existe opção errada, apenas pontuações. No final, o total de pontuação do usuário varia pelas opções marcadas, encaixando ele em algum grupo de pontuações esperadas. O que acha de desenrolar esse desafio?

---

E por hoje é só! Curtiu? Comenta e compartilha!! <s>Como falei no início, temos outros desafios lá no canal do Discord. Pode chegar que a casa é aberta, rs</s>. Qualquer coisa, é só falar aí nos comentários!

Abraços...

Inté!
