LoginSignup
0
1

More than 1 year has passed since last update.

【育児負荷低減の為のエンジニアリングその2 ポケモンしりとり編 AWS S3 lambda api-gateway terraform】

Last updated at Posted at 2021-05-08

何を作ったか

  • ポケモンの名前を入力すると、自動的にしりとりをしてくれる物を作りました。
  • AIとかそんな大それたもの入ってないです。ランダムで対象のポケモンを繋いでいくものです。

スクリーンショット 2021-05-04 11.16.png

しりとりモード

  • ふつうにしりとり
    • 最初と最後のポケモンを入力すると勝手にしりとりやってくれます
  • にゅうりょくもじからはじまるポケモン
    • 入力文字から始まるポケモンを返してくれます
  • どこかにもじがはいるポケモン(尻じゃないですが)
    • 入力文字が含まれているポケモンを返してくれます
  • にゅうりょくもじでおわるポケモン
    • 入力文字で終わるポケモンを返してくれます

何故作ろうと思ったか

https://qiita.com/zukaishi/items/7bcb16fb57fdcd762914
- 前回のこちらと同じ理由になりますが、あまりにもしりとりをする事を要求されることに疲れました
- ポケモンにハマっている息子(小学1年生)がたくさんポケモンの名前を覚えたのでそれをアピールしたいんでしょうね。そのためしりとり、毎日のように勝負を挑んでくるが面倒になってしまったので、ポケモンしりとりを勝手にしてくれるものをつくりました。
- そんなにポケモン知らないし、そもそも赤・緑で知識が止まっている状態ですし、、、。

どのように実現したか

アーキテクチャ

Untitled Diagram (2).png

  • AWSとTerraformの勉強がてらこんなアーキテクチャで作ってみました。
  • AWSでホスティングさせるための実際にブラウザでアクセスするindexとしりとりの対象にするリストをあらかじめs3 bucketにあらかじめ格納しておく
  • しりとりの核となる処理は、API-Gateway → LambdaでAPIリクエストすることによって処理される
  • 取得された結果をブラウザが表示する

AWS準備

variable "bucket_name" {
  default = "website.shiritori.com"
}

data "aws_iam_policy_document" "s3_bucket_policy" {
  statement {
    actions = ["s3:GetObject"]
    effect  = "Allow"
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    resources = ["arn:aws:s3:::${var.bucket_name}/*"]
    sid       = "PublicReadGetObject"
  }
}

resource "aws_s3_bucket" "website_shiritori_com" {
  bucket = var.bucket_name
  policy = data.aws_iam_policy_document.s3_bucket_policy.json

  website {
    index_document = "index.html"
    error_document = "error.html"
  }
}

resource "aws_s3_bucket_public_access_block" "website_shiritori" {
  bucket                  = var.bucket_name
  block_public_acls       = true
  block_public_policy     = false
  ignore_public_acls      = true
  restrict_public_buckets = false
}

output "url" {
  value = aws_s3_bucket.website_shiritori_com.website_endpoint
}

データ取得

func comprised(word string) string {
    url := "https://s3-ap-northeast-1.amazonaws.com/website.shiritori.com/data/pokemon_list.csv"
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println(err)
    }

    defer resp.Body.Close()
    reader := csv.NewReader(resp.Body)
    pokemonList := map[int]string{}
    for {
        record, err := reader.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        var i int
        i, _ = strconv.Atoi(record[0])
        pokemonList[i] = record[1]
    }

    result := ""
    for key, value := range pokemonList {
        if strings.Contains(value, word) {
            result += fmt.Sprintf("%d:%s", key, value)
            result += ","
        }
    }
    return result
}

メイン部

func shiritori(name1 string, name2 string) string {
    url := "https://s3-ap-northeast-1.amazonaws.com/website.shiritori.com/data/pokemon_list.csv"
    resp, err := http.Get(url)
    if err != nil {
        fmt.Println(err)
    }

    defer resp.Body.Close()
    reader := csv.NewReader(resp.Body)
    pokemonList := map[int]string{}
    for {
        record, err := reader.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        if name2 == record[1] || getLastString(record[1]) != "ン" {
            var i int
            i, _ = strconv.Atoi(record[0])
            pokemonList[i] = record[1]
        }
    }

    startNo := contains(pokemonList, name1)
    endNo := contains(pokemonList, name2)
    if startNo == 0 || endNo == 0 {
        fmt.Printf("input name failed ¥n")
    }

    word := ""
    no := startNo
    result := ""
    for {
        result += fmt.Sprintf("%d:%s", no, pokemonList[no])
        word = pokemonList[no]
        delete(pokemonList, no)

        lastString := getLastString(word)
        list := containsList(pokemonList, lastString)
        if len(list) == 0 || endNo == no {
            break
        }
        result += ","
        if lastString == getRuneAt(name2, 0) {
            result += fmt.Sprintf("%d:%s", endNo, name2)
            break
        }

        randMap := []int{}
        for key := range list {
            randMap = append(randMap, key)
        }
        rand.Seed(time.Now().UnixNano())
        randNo := rand.Intn(len(randMap))
        no = randMap[randNo]
    }
    return result
}

文字変換周り

func getLastString(word string) string {
    // 対象文字の最後の文字を取得utf-8のため、/3している
    lastString := getRuneAt(word, len(word)/3-1)

    // 最後の文字が伸ばし棒の場合
    if lastString == "ー" {
        lastString = getRuneAt(word, len(word)/3-2)
    }

    // 特殊文字、捨て仮名を扱いやすい形へ変換する
    r := strings.NewReplacer("♂", "ス", "♀", "ス", "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "ュ", "ユ", "ャ", "ヤ", "ョ", "ヨ")
    resStr := r.Replace(lastString)
    return resStr
}

コード

使い方

結果どうなったか

  • その度にiPadをせがまれるようになって助かったのはいいのですが、僕がその時間iPad使えないのでちょっと不便、、、。
  • 子供はキーボード叩いたりできないのでちょいちょい打ち込むのが面倒
  • あと、ポケモンのブームが過ぎ去って、今度はLEGOブームが到来です。
  • LEGOを自動化するものは、、、。ハードルが高いなぁと思う今日この頃です。

今後やりたいこと

  • 最短、最長の経路を選択できるようにしたい
  • イラストとか引っ張ってこれるようにしたい
  • terraformでapi-gateway周りも実現できるようにしたい
  • ポケモン名前を選択できるようにする or 音声入力を可能にするか

修正点

  • 独自ドメイン対応
0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1