[Go] Create a tool that returns the Pokemon race value received from the standard input

Hello, this is Yamauchi! A fat can was held at our Fukuoka branch on November 14, 2020. It's a name that seems to be something like staying overnight, but although I've been doing it for at least one month for about a year and a half, I still only have a day trip. .. .. Well, let's put it aside, and I'll introduce the tool I made this time! By the way, I'm a super beginner about Go language, and it's the first time I've made something like a tool, so I think there are various problems with the code.

A rough introduction to this tool

For this tool, I created the API and the side that hits it separately. This is the repository. API:https://github.com/y-keisuke/pokemon Hitting side: https://github.com/y-keisuke/pokemon_command Please look at it with the feeling that you will review it w

I really wanted to put them together in one repository, but I didn't know if it was possible, so I divided the repositories, so only the commands I used later have commands in the repository. It doesn't matter.

That's why it's a rough introduction, so the movement is like this ↓ (Sorry for the sluggish movement, I hit a total of 4 times) pokemon.gif

It's pretty rough, but it looks like this.

Introduction on the API side

First of all, on the API side, I borrowed json from the repository here ↓. https://github.com/fanzeyi/pokemon.json/blob/master/pokedex.json

db.go I really wanted to prepare a DB, but it was troublesome, so I used json as it is ← That is db.go.


package db

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

type PokemonCollection struct {
	Pokemons []PokemonData
}

type PokemonData struct {
	Id   int         `json:"id"`
	Name PokemonName `json:"name"`
	Type []string    `json:"type"`
	Base PokemonBase `json:"base"`
}

type PokemonName struct {
	English  string `json:"english"`
	Japanese string `json:"japanese"`
	Chinese  string `json:"chinese"`
	French   string `json:"french"`
}

type PokemonBase struct {
	HP        int `json:"hp"`
	Attack    int `json:"attack"`
	Defense   int `json:"defense"`
	SpAttack  int `json:"spattack"`
	SpDefense int `json:"spdefense"`
	Speed     int `json:"speed"`
}

func GetPokemonCollection() PokemonCollection {
	raw, err := ioutil.ReadFile("./pokedex.json")
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	var pokemonCollection PokemonCollection
	json.Unmarshal(raw, &pokemonCollection)

	return pokemonCollection
}

Some of the structures defined above are exactly the same as the json structure. The structure has changed a little from the borrowed json. The GetPokemonCollection () defined here puts the json data into the structure. In particular

json.Unmarshal(raw, &pokemonCollection)

This part is packed in the structure. This structure is used as a DB.

main.go

Next is main.go.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	pokemon2 "pokemon/pokemon"
)

func pokemonToJson(w http.ResponseWriter, r *http.Request) {
	name := r.FormValue("name")
	pokemon, err := pokemon2.GetPokemonBy(name)

    //I didn't know how to return the error information json nicely here
	if err != nil {
		log.Writer()
		http.Error(w, fmt.Sprintf("{\"err\":\"%s\"}", err), 200)
		return
	}
	pokemonJson, _ := json.Marshal(pokemon)
	fmt.Fprint(w, fmt.Sprintf("%+v", string(pokemonJson)))
}

func handleRequests() {
	http.HandleFunc("/", pokemonToJson)
	log.Fatal(http.ListenAndServe(":18888", nil))
}

func main() {
	handleRequests()
}

What we are doing here is "listen for access on port 18888, look at the parameters of that access, get the Pokemon data, and return the race value in json".

First, the part waiting at port 18888

//Listen to port 18888
http.ListenAndServe(":18888", nil)

Then look at the access parameters

//Get the parameter of key called name
name := r.FormValue("name")

In addition, get Pokemon data

//Get Pokemon with GetPokemonBy in pokemon2 (details below)
pokemon, err := pokemon2.GetPokemonBy(name)

Finally, json returns the race value

//Convert struct to json and then return
pokemonJson, _ := json.Marshal(pokemon)
fmt.Fprint(w, fmt.Sprintf("%+v", string(pokemonJson)))

Go? What can you eat it? I'm likely to hear a voice saying "I'm printing without returning!", So I'll add that Fprint is a function that allows you to specify the writing destination, and this time I'm writing to w (http.ResponseWriter). , It feels like it will be returned as a result response.

pokemon.go

This is the one that says pokemon2. It is 2 because I covered it with the project name. It's a pretty NG name. goland did a good job ...

package pokemon

import (
	"errors"
	"pokemon/db"
)

type Pokemon struct {
	Name      string `json:"name"`
	HP        int    `json:"hp"`
	Attack    int    `json:"attack"`
	Defense   int    `json:"defense"`
	SpAttack  int    `json:"sp_attack"`
	SpDefense int    `json:"sp_defense"`
	Speed     int    `json:"speed"`
}

func GetPokemonBy(name string) (*Pokemon, error) {
	pokemonCollection := getPokemonCollection()
	for _, pokemon := range pokemonCollection.Pokemons {
		if pokemon.Name.Japanese == name {
			return getPokemonStruct(pokemon), nil
		}
	}
	return nil, errors.New("Pokemon not found")
}

func getPokemonCollection() db.PokemonCollection {
	return db.GetPokemonCollection()
}

func getPokemonStruct(pokemon db.PokemonData) *Pokemon {
	return &Pokemon{
		Name: pokemon.Name.Japanese,
		HP: pokemon.Base.HP,
		Attack: pokemon.Base.Attack,
		Defense: pokemon.Base.Defense,
		SpAttack: pokemon.Base.SpAttack,
		SpDefense: pokemon.Base.SpDefense,
		Speed: pokemon.Base.Speed}
}

For the time being, I will only explain the function called GetPokemonBy that was called from main.go.

//I'm getting a DB
pokemonCollection := getPokemonCollection()

I get it via a private function, but I don't remember why.

for _, pokemon := range pokemonCollection.Pokemons {
	if pokemon.Name.Japanese == name {
		return getPokemonStruct(pokemon), nil
	}
}

If the name of the acquired DB Pokemon and the name of the Pokemon received in the parameter match, that Pokemon is packed in a new structure and returned.

Roughly speaking, the API side looks like this.

Hitting side

Next is the side that hits the API.

main.go

For the time being, the source code.

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"pokemon_command/input"
	"pokemon_command/pokemon"
)

func main() {
	url := input.CreateUrl()

	resp, _ := http.Get(url)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)

	var errCheck map[string]string
	json.Unmarshal(byteArray, &errCheck)
	if val, ok := errCheck["err"]; ok {
		fmt.Println(val)
		return
	}

	pokemonStruct := pokemon.JsonToPokemon(byteArray)

	pokemon.PrintPokemon(pokemonStruct)
}

This is an explanation of the contents of the main function.

//Details of input will be described later
url := input.CreateUrl()

Here, the standard input is received and the URL for hitting the API is generated based on it.

resp, _ := http.Get(url)
defer resp.Body.Close()

Then access the generated URL and receive the result.

byteArray, _ := ioutil.ReadAll(resp.Body)

Gets the Body of the received response.

var errCheck map[string]string
json.Unmarshal(byteArray, &errCheck)
if val, ok := errCheck["err"]; ok {
	fmt.Println(val)
	return
}

I'm doing error handling here, but I don't know the optimal solution. .. ..

//pokemon will be described later
pokemonStruct := pokemon.JsonToPokemon(byteArray)
pokemon.PrintPokemon(pokemonStruct)

The last received json is stored in the structure and then output.

main.go looks like this.

input.go

package input

import (
	"bufio"
	"fmt"
	"os"
)

func CreateUrl() string {
	fmt.Print("Please enter the name of the Pokemon.\n>> ")
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
	return "http://localhost:18888/?name=" + scanner.Text()
}

It's not a big deal here, it accepts standard input and generates a URL based on that input.

fmt.Print("Please enter the name of the Pokemon.\n>> ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()

These three lines display the text that prompts you to enter, and the standard input is obtained by the function of the bufio package.

return "http://localhost:8081/?name=" + scanner.Text()

The URL is generated by combining the acquired character strings. I want to write something a little better.

pokemon.go

package pokemon

import (
	"encoding/json"
	"fmt"
	"log"
)

type Pokemon struct {
	Name string `json:"name"`
	HP int `json:"hp"`
	Attack int `json:"attack"`
	Defense int `json:"defense"`
	SpAttack int `json:"sp_attack"`
	SpDefense int `json:"sp_defense"`
	Speed int `json:"speed"`
}

func JsonToPokemon(pokemonJson []byte) *Pokemon {
	pokemon := new(Pokemon)
	err := json.Unmarshal(pokemonJson, pokemon)
	if err != nil {
		log.Fatal(err)
	}
	return pokemon
}

func PrintPokemon(pokemon *Pokemon) {
	fmt.Println("Name: ", pokemon.Name)
	fmt.Println("HP     : ", pokemon.HP)
	fmt.Println("Attack: ", pokemon.Attack)
	fmt.Println("Bougyo: ", pokemon.Defense)
	fmt.Println("Tokukou: ", pokemon.SpAttack)
	fmt.Println("Tokubo: ", pokemon.SpDefense)
	fmt.Println("Quickness: ", pokemon.Speed)
}

Again, it's not a big deal, just a function that packs the received json into a struct and a function that outputs based on that struct. I haven't written anything really important, so I'll omit the details.

Impressions

It became an article that felt quite awkward, but ← By trying to make something with GO, I wanted to do something like "accept input with standard input" this time, so for the time being, I'm glad that I could implement it like that. Ideally, create something like the pokemon command, and the display will change depending on the option. I want to make something like that, but I will do my best at another time.

I wanted to hit the API anytime, anywhere, not just locally, so I signed up for Sakura VPS and put the API there, so I'll write about that in the article next time.

That's it!

Recommended Posts

[Go] Create a tool that returns the Pokemon race value received from the standard input
[Discode Bot] I created a bot that tells me the race value of Pokemon
Create a data frame from the acquired boat race text data
[Django] Create a form that automatically fills in the address from the zip code
Create a translation tool with the Translate Toolkit
Have python parse the json entered from the standard input
A programming language that protects the people from NHK
# Function that returns the character code of a string
Create a chatbot that supports free input with Word2Vec
Create a bot that only returns the result of morphological analysis with MeCab on Discord
An example of a mechanism that returns a prediction by HTTP from the result of machine learning
I made a tool to automatically generate a simple ER diagram from the CREATE TABLE statement