Pokémon Shirt が届いた!!

www.pokemon.co.jp

先行で注文販売していたPokémon Shirtが届きました!
第一世代のポケモンを元にしたデザインのシャツをオーダーできます。

カジュアル、ドレス、アロハを選べ、ドレスは首回りやゆき丈を指定可能。
シャツ全体のデザインはもちろん、襟、袖口、ポケットなども変更できます。
それぞれのパーツに好きなポケモンをあしらうことができます。ヒトカゲリザードリザードンと進化先のポケモンに統一したり、なんてことも。ニョロゾ系が統一感があります。
ドレスシャツでも、袖口や襟の裏のみで目立たないようにできるので、フォーマルでも行けるかも。

私は、とりあえずドレスシャツで定番のピカチュウにしました。全体は白いシャツですが、ポケットと襟の裏、袖口の裏などにピカチュウを隠してみました。

f:id:lastline:20190301182702j:plain
箱のデザインもいいですね。

f:id:lastline:20190301182721j:plain
先行特典として、ハンカチを頂けました。ペイズリー型のコクーンはセンスありますよね。

f:id:lastline:20190301182740j:plain
オーダー通りに、ワンポイントでピカチュウが入っているので、フォーマルでも行ける!

ゆるドミで遊んだボドゲの感想

主催のしんざきさんが週の初めから体調が悪そうで心配しておりましたが、やはり働きながら風邪を治すのは無理だったようで、ゆるドミ途中でのリタイアとなりました。会場設営くらいしか手伝ってないのに、名前が挙がっており恐縮です。息子さんがドミニオンスイスドローに参加できず残念がったおりました。
ドミニオン以外にも色々なボードゲームで遊べるのも、ゆるドミの魅力だと思います。あと、くるりんパニック世界大会。
自分的な心残りは、ブラフが一回したできなかったことですね。自分のを持ってくるべきでした。

忘れない内にプレイしたボードゲームの感想を書いて起きます。

デクリプト

今回一番面白いと思ったボードゲーム。チームに分かれて、相手のキーワードを推理するゲームが、キーワードそのものを当てる必要がないのが面白いと感じました。ゲーム終了後にキーワードの当てっこをするのも楽しみの一つかも。
インストを聞いていると、いまひとつイメージが掴めませんが、実際にプレイしてみるとルールは単純だと分かります。

f:id:lastline:20190223162920j:plain
デクリプトのキーワード表示板。コンソールみたいでかっこいい!

それぞれのチームに4つのキーワードが指定されます。自分のチームのキーワードはみることができますが、相手のキーワードは見ることができません。キーワードは以下のように番号と紐付けられます。

1番「うさぎ」
2番「大会」
3番「司会」
4番「コンビニ」

各チームで一人ずつ暗号作成者が3つの番号が書かれたカードを引きます。たとえば「1、2、4」など。この番号の順番を正確に味方チームへ伝える必要があります。伝達に二回失敗すると負けです。そして、相手チームはその順番を二回当てると勝ちです。
暗号作成者は、この3つの番号の順番を味方チームだけにわかるようなキーワードで伝えます。例えば、「跳ねる」、「ゲーム」、「街」など。キーワードを知っていれば「跳ねる」は1番の「うさぎ」、「ゲーム」は2番の「大会」、「街」は4番の「コンビニ」と連想できるでしょう。相手チームは、このキーワードを元に番号の順番を推測するのですが、1ターン目は手掛かりがないので無理です。キーワードがどの番号に相当したかメモっておきましょう。勝負は2ターン目からです。
2ターン目が「3、4、1」で暗号作成者が「声」、「店」、「月」と伝達します。味方チームなら暗号を解読できますが、相手チームは推測する必要があります。たとえば次のように推測したとします。「声」は動作なので「跳ねる」と関連していそうだ。「店」は街中にあるので「街」と関連があるだろう。「月」は良く分からないので、まだ出ていない3番目に相当しそうだ。以上の推測をまとめると以下の通り。

一番「跳ねる」「声」
二番「ゲーム」
三番「月」
四番「街」「店」

「1,3,4」と推測しましたが、残念ながら不正解で暗号解読には至りませんでした。キーワードが溜まっていくと推測しやすくなります。つまり、暗号作成者は推測されにくいキーワードを考える必要がでてきます。しかし、あまりにも突飛なキーワードでは味方チームに伝わりません。相手チームのキーワードの推測や、推測されにくいキーワードを考えるのが肝ですが、暗号作成者の変化球なキーワードを推測するのも楽しみの一つでしょう。
実際のルールではキーワードを考える時間、解読する時間に制限があるのですが、今回は初回プレイなのもあって時間は測りませんでした。制限時間がないと、相手チームにばれにくいキーワードを考えられるため、必然的にプレ時間も長引きます。ある場合は、キーワードが類推しやすくなるでしょうし、伝達ミスも増えるので、ゲーム性が異なってくるでしょう。

面白さが伝わったがあまり自身はありませんが、結構盛り上がっていましたし、プレイする人にっよって連想されるキーワードも異なるので、何度もプレイしたくなるゲームです。

タイムボム

時空警察とボマーに振り分けられ、お互いの役割は伏せたまま、それぞれの陣営の目標を達成するゲーム。時空警察は時空爆弾の解除を目指し、ボマーは爆発を狙う。インストでは人狼系と言われて納得。

プレイヤーに「解除」、「しーん」、「爆発」と書かれたカードが所定の枚数配られます。プレイヤーはカードの内容を把握して、その後にシャッフルして目の前に伏せます。つまり、自分が持ってるカードの種類は把握できるけど、どの位置かは分からない状態です。ニッパーを持ったプレイヤーが自分以外の伏せカードを選び表にします。「解除」なら爆弾を解除でき、全ての爆弾を解除できれば時空警察の勝ち。「爆発」を引いたらボマーの勝ち。「しーん」は何も起きません。ここは運要素ですが爆弾解除というフレーバーが活きてますね。
「爆発」以外なら、カードをめくられた人が次のニッパー係で、それぞれのプレイヤーが一枚ずつめくられるまで続けます。一巡したら、そカードを一旦集めて配り、仕切り直します。

パーティーゲームとして、みんなでワイワイやるのに向いてると思います。デザインやフレーバーからしても子どもと一緒に遊ぶのに丁度良いでしょう。

レールロード・インク

地図ボードに線路と道路を引くゲーム。
線路と道路の書かれたサイコロを振って、その出目に応じた路線を地図ボードに書き込んでいきます。ルールに沿ってマスを埋めて高得点を狙うパズルゲームです。実質的にはソロプレイですが、出来上がった路線を見ると引き方や書き方に個性がでて面白いです。

シンプルで気軽に遊べ、サイコロによるランダム要素があるので毎回異なる展開になるので長く遊べそうです。拡張ルールとしては、ボードの地形が変化し線路を引けないマス目などが生じるようです。

ボードゲームFallout

Falloutの世界観やゲーム性が、ボードゲームで再現されていました。マップを探索し、敵と戦ったり回避しながら建物を漁り、ジャンクを売りを繰り返しながらシナリオを進めていくのはまさにFallout。デスクローも徘徊しているぞ!!もともと、TPRGっぽいので相性がよいのでしょう。奴隷商人やレールロードなどのFallout 3や4のシナリオが再現されています。
ゲームの目的はイベントを進めてポイントを獲得すること。誰かが8点かせげば終了。終了時に特定の条件を達成していると追加のポイントが貰えるため、8点に到達したプレイヤーが勝利するとは限りません。

戦闘やイベントなどは、基本的にはサイコロの出目で判定を行います。特定のSPECIALを有しているとサイコロの振り直しができます。例えば、交渉ではCharismaであるCが、ロボットやコンソールの操作ではIntelligenceのIがあるとサイコロを振り直せます。装備がSPECIALを補ったり、サイコロを振り直すなど、特定の効果を有しています。
武器としては銃がやはり強いです。弾切れの心配はありません。まぁ実際のゲームでも弾切れすることはほぼないですけど。防具としては、パワーアーマーがあればほぼ死にません。ユニーク装備として所有しているキャラクターもいますが、移動力が下がるので一長一短。強力なパワーアーマーが手に入るイベントもありますが、それを達成するのは時間も手間もかかります。

Perkも用意されているのですが、今回のプレイではみんなでイベントこなすのに一直線だったのでPerkを利用する機会はありませんでした。ゲームの終了条件である8点に到達するのが難しかったので、もうちょっと敵と戦ったり、建物を漁ったりすべきだったのかな?

プレイヤー同士でアイテムの交換もできるので、ある意味でマルチプレイFalloutであり、76っぽくあります。およそ3,4時間くらいで終わるので、この手のゲームとしては早い方かと思います。他のシナリオもプレイしてみたいです。

紫禁城

BMマガジンにおまけでついていた陣取りゲームです。赤、青、緑、黄と4種類のカードがあり、プレイヤーはそれぞれ一色を選び、そのカードセットのみを使用します。
一枚ずつカードをめくり皇帝の部屋につなげていきます。部屋のタイルは、赤、白、青の三色でつなげられるのは同じ色のタイルのカードのみです。カードには、タイルの色と壁や門の他、点数がアップする龍や、各プレイヤーの色に対応した宦官などが描かれています。
壁で仕切られた部屋ができたら、その度に点数計算をします。宦官を最も多く配置した人がその部屋の主となり得点を得ます。たとえば、赤の宦官が3人、青の宦官が1人なら、赤のプレイヤーが得点を得られます。タイルの枚数ごとに1点、また龍がいると3点です。さらに、その出来上がった部屋に門で繋がっている部屋のタイルも点数に含まれます。広い部屋を作りつつ、門でつながる部屋を作るのが大量得点の秘訣です。ただし、門を作りすぎると相手に大量得点のチャンスを与えることにも鳴るので難しい所です。
例えば、完成した大きな部屋に門があり、そこに1タイルだけで部屋を完成させることができる3方向が壁のカードを配置すると、完成した部屋の点数は1点ですが、大きな部屋の得点も加算されるので大量に得点が得られます。

カードを一枚ずつめくり、それを置くしかできないため運要素が強いと感じました。もともとが惑星開拓をモチーフにしたゲームなので出たとこ勝負なのでしょうが、皇帝と宦官なら調略要素が欲しいところ。カードを2から3枚まで保持できるとか、1枚だけ表を向けて保持できるとか、ルールのアレンジをしてみも良いかも。
ルールをアレンジするのは楽しむとして、不満点としては得点チップが1,2,10,20点と5点がないので、点数を配分するときに面倒でした。

AZUL

綺麗なタイルを作るゲーム。新版が出ているのを知り、ちょっと悲しい。

f:id:lastline:20190223185117j:plain
AZULの装飾スペースに配置されたタイル

5色のタイルを集めて、5×5のボードに装飾していきます。各タイルとは所定の位置に配置しなければなりません。それぞれの行と列には一色しか同じ色を配置できません。列をつなげたり、色を揃えることで点数を獲得できます。横一行に揃えたプレイヤーが出たらゲーム終了です。
プレイヤー人数に応じた枚数の皿を用意し、袋から4枚ずつタイルを取り出し乗せます。各プレイヤーは一つの皿から一色だけをめとめて取ることができます。そのタイルをボードに配置し、残ったりタイルは中央にまとめます。中央にまとめられたタイルも取ることができます。場が進むにつれて皿に盛られていないタイルが増えます。
ボードには、皿から取ってきた複数枚のタイルを置く確保スペースと、装飾スペースがあります。確保スペースには上から行に応じて1、2、3、4、5枚の空きの行があり、手番で取ったタイルを特定の行に配置します。たとえば、赤を2枚取った手番で1行目に1枚、2行目に1枚と置くことはできません。
2行目ならば、2枚置けます。1行目に置く場合は余るので、その場合はペナルティとしてボードの一番下にある行に配置します。一番下はマイナス行で、置くと減点となります。手番が進むと、お皿に盛られていないタイルがどんどん増えていきます。4枚や5枚のタイルを確保できるチャンスですが、徐々に確保スペースがなくなるため、後半ほどマイナスが嵩みやすい。
取れるタイルがなくなったら、確保スペースのタイルを装飾スペースに配置します。1行目なら1枚、2行目なら2枚と、行に応じた枚数を置いていれば、装飾スペースの各色のスペースに配置できます。足りない場合は、そのまま残します。例えば、4行目に3枚しか無い場合などですね。
装飾スペースに置ければ1点を確保できます。タイルが連続すると、連続した枚数に応じて点数が入ります。最後に、マイナス点に配置されたタイルに応じて減点します。
タイルと配置したら、残ったタイルは箱などに待避させておいて、袋が空になったら入れます。
2巡目以降は、装飾スペースにタイルが配置されています。装飾スペースにあるのと同色のタイルを確保スペースの行に置くことはできません。例えば、黄色のタイルが既に3行目の装飾スペースにあったら、3行目の確保スペースに置くことはできません。つまり、徐々に確保スペースにおけるタイルの色が限定されていきます。つまり、減点になる可能性が増えていきます。

タイルの出方はランダムですが、それ以外は公開情報なので目的が筒抜けです。他のプレイヤーがやりたいことが類推しやすいので、その点を考えて立ち回る必要があります。ちょうどよい複雑さになっていると思います。タイルの質感がよく、ずっと触っていたくなり、また見た目も綺麗なゲームです。

ラーメン屋は何曜日に休みが多いのかAPIを利用して調べた

きっかけはツイート

ラーメン屋は何曜日に休みのお店が多いのかを調べてみようと思ったきっかけは、フォロワーのツイートだった。調べることは好きだけど、これまでは人力で調べてばかりだった。Pythonを勉強してから、ずっとウェブスクレイピングやWeb APIに興味があったので、折角なので挑戦してみた。

ラーメン屋は月曜日が休みが多い

HOT PEPPERぐるなびAPIを提供しているので、そこからラーメン店の情報を取得し、定休日を集計した。

データは2019年2月21日に取得し、HOT PEPPERからは2212件、ぐるなびからは4767件の店舗情報が集められた。これを元に、定休日を集計した。データはJSON形式で、定休日もキーとして割り振られているので、抽出は簡単だった。

HOT PEPPERぐるなびともに、年中無休がもっと多く全体の30%から40%を占めている。曜日としては、月曜日が休みの店が多くHOT PEPPERでは15%、ぐるなびでは11%であった。次いで、火曜、水曜、日曜が10%前後である。金土休みの店はほとんどなく、0.5%程度である。それを除けば、木曜休みが5%と、他の曜日に比べて半分程度であった。
無休と曜日別の定休日はそれぞれのサービスで似たような傾向にあったが、その他は異なる結果になった。それぞれのサービスに登録する店舗の傾向を反映しているのだろう。たとえば、不定休の店はHOT PEPPERでは6%だが、ぐるなびでは14%と倍近い違いがある。一方で、HOT PEPPERでは祝日前が定休日の店舗が2%程度なのに、ぐるなびには殆どなかった。

f:id:lastline:20190222121019p:plain
HOT PEPPER ラーメン屋 定休日

f:id:lastline:20190222121057p:plain
ぐるなび ラーメン屋 定休日

曜日別でも割合を算出した。何曜日に休みが多いのかは、こちらの方が適当だろう。月曜日が定休日の店が25%から30%で、火曜、水曜、日曜が大体20%くらいとなる。月曜休みの未成は多いが、突出して多いわけではない。火、水、日はHOT PEPPERぐるなびとでばらつきがあったので、月曜日の次に多い曜日を特定するのは難しい。一方で、木曜日は10%と下がる。金土は稼ぎ時のためか、定休日のお店は1%くらいである。むしろ、数%でも存在していることが意外であった。
日曜日が休みの店舗はオフィス街などにあるのだろう。平日に休む場合は、月曜日が多く、次いで火曜日と水曜日となる。月曜日が休みなのは多客日の後に休みたい心理的な面と、日曜に市場が開いていないため食材調達上の問題もありそうだ。木曜日が定休の店が少ないのは、金曜日の仕込みなどがあるからだろうか。

f:id:lastline:20190222121128p:plain
HOT PEPPER ラーメン店 曜日別定休日

f:id:lastline:20190222121202p:plain
ぐるなび ラーメン屋 曜日別定休日

他の業態の飲食店と比較すべきだが、今回は本筋ではないのでやらない。恐らく、他の飲食店でも似たような傾向になるだろう。

Pythonによる定休日の集計方法

HOT PEPPERぐるなびAPIを利用し、Pythonにてラーメン店の情報を取得しJSONとして保存した。そのデータから定休日の情報を抜き出し、集計を行った。
取得したJSONデータから直接定休日を集計した方が手順は省けるが、定休日以外の統計処理を見据えて店舗情報を保存することにした。

APIの叩き方は経験が無かったので少々戸惑ったけども、一度取得できれば試行錯誤は用意だった。むしろ、JSONデータや定休日を集計しCSV形式で保存したデータが文字化けしまくって四苦八苦した。

HOT PEPPERぐるなびAPI仕様の所感

食べログAPIがあると便利そうだと思ってググったら、残念ながらかなり前に廃止されていることを知った。そこで、食べログAPI提供終了していたので代わりのAPIを探した(一覧) - エンジニアステップ を参考に、HOT PEPPERぐるなびAPIを使うことにした。それぞれに一長一短がある。

HOT PEPPER はメールアドレスのみでAPIキー取得でき、お試しにはもってこいだ。検索できる店舗数に上限がないのも扱いやすい。また、店舗のジャンルコードとして「ラーメン店」を指定できるため、今回の用途と相性もよい。ただし、ぐるなびと比較すると登録店舗数が圧倒的に少ない。
データ形式XMLがデフォルトだが、JSONJSONPも選択できる。

ぐるなびは、APIを取得するために氏名などを入力する必要があるため、お試しで使う身としてはちょっと敷居が高い。一方で、自身でサービスを立ち上げる際にはぐるなびの方が案内がしっかりしており信頼感が高い。データはJSON形式で出力される。

業態コードとして「ラーメン」が用意されているものの、それを指定して叩いても居酒屋のようなラーメン専門店以外もひっかかる。特定の業態を抽出するには工夫が必要となる。
一方でテストツールをウェブから利用でき、お試しで結果を得やすい。また登録店舗数が圧倒的に多い。たとえば、ラーメン店で検索するとHOT PEPPERでは2,600店しかヒットしないが、ぐるなびだとその10倍の26,000店もヒットする。統計を取りたいならぐるなびの方に分があるだろう。ただし、取得上限が1,000件までのため、必要な情報を網羅的に得るのにも工夫が必要となる。

PythonAPIを叩いてみれば

Python の requests を用いて API を叩き、JSON形式で店舗情報を取得した。先にも述べたが、ラーメン店を絞り込むのにやや工夫が必要である。

HOT PEPPER はラーメン店のジャンルコードが G013 と指定されており、絞りこみが楽である。約2,600店がヒットした。取得上限はないが1ページ当たり100店舗までしか表示できないので、26回叩いて全店舗の情報をJSONとして出力した。

ぐるなびにもラーメン店の業態コードがあるものの、それだけではラーメン店以外もヒットする。お好み焼きやしゃぶしゃぶ店なども引っかかるため、ぐるなびの分類方法は難がありすぎに思う。対処は比較的簡単で、フリーワードに「ラーメン」を入れることでほぼラーメン店のみに絞れた。
ぐるなびでは約26,000件ものラーメン店がヒットした。取得上限が1,000件なので、それ以上の店舗情報を得るには叩き方に工夫が必要となる。今回は、都道府県毎に取得した。
東京には3,000店舗以上、都市圏では1,000店舗以上のラーメン店が登録されている。 1,000件以上ある都道府県では上限の1,000件を取得するように設定した。ぐるなびの掲載順に取得すると、ラーメン店以外は検索結果の後ろの方に表示される。上位1,000件を取得した方が、ラーメン店以外を除外しやすい。

APIの叩き方に少々手間取ったが、それ以上に四苦八苦したのがJSONデータの保存である。エンコードUTF-8を指定しているのに、なぜか日本語がASCIIで描き込まれ uXXXXなどど保存される。それを回避するには、open() ではなく codecs.open() で開き、dumps() の引数に ensure_ascii=False と指定する必要があった。

# HOT PEPPER のラーメン店情報をJSON形式で取得する
import requests
import json
import codecs
 
# APIキーの指定
apikey = ”{個々人のAPIKEYを入力する}”
 
# APIのURL
api = 'http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key={key}&order=3&genre={genre}&count={count}&start={start}&format=json'
 
# APIにリクエストを送信して店舗数を取得する
url = api.format(key=apikey, count=1, start=1, genre='G013')
response = requests.get(url)
response.raise_for_status()
data = json.loads(response.text)
count_range = data['results']["results_available"]
 
# ラーメン店の検索結果をJSON形式で取得する
ramen_json = []
 
# 100件ごとにAPIにリクエストを送信して店舗情報を取得する
for i in range(0, count_range, 100):
    url = api.format(key=apikey, count=100, start=i, genre='G013')
 
    response = requests.get(url)
    response.raise_for_status()
 
    data = json.loads(response.text)
    data = data['results']['shop']
    ramen_json += data
 
# 取得したラーメン店のJSONデータを保存
with codecs.open('ramen_hot.json', 'w', 'utf-8') as obj:
    dump = json.dumps(ramen_json, indent=4, ensure_ascii=False)
    obj.write(dump)
# ぐるなびのラーメン店情報をJSON形式で取得する
import requests
import json
import codecs
 
# APIキーの指定
apikey = ”{個々人のAPIKEYを入力する}”
 
# APIのURL
api = 'https://api.gnavi.co.jp/RestSearchAPI/v3/?keyid={key}&category_s={categoly}&freeword={freeword}&offset={start}&hit_per_page={count}&pref={pref_code}'
 
# 業態コードとフリーワードの指定
w_ramen = '%e3%83%a9%e3%83%bc%e3%83%a1%e3%83%b3' # ラーメン
c_ramen = 'RSFST08008' # ラーメン
 
# 都道府県毎に店舗情報を取得する
for num in range(1, 48):
    num = str(num).zfill(2)
    pref_num = 'PREF' + num
 
    # APIにリクエストを送信して店舗数を取得する
    url = api.format(key=apikey, count=1, start=1, freeword=w_ramen, categoly=c_ramen, pref_code=pref_num)
    response = requests.get(url)
    response.raise_for_status()
    data = json.loads(response.text)
    count_range = data['total_hit_count']
 
    # 上限が1000件なので、1000を超える場合は count_range を1000に書き換える
    if count_range > 1000:
        count_range = 1000
 
    # ラーメン店の検索結果をJSON形式で取得する
    ramen_json = []
 
    # 100件ごとにAPIにリクエストを送信して店舗情報を取得する
    for i in range(1, count_range, 100):
        url = api.format(key=apikey, count=100, start=i, freeword=w_ramen, categoly=c_ramen, pref_code=pref_num)
 
        # 実際にAPIにリクエストを送信して結果を取得する
        response = requests.get(url)
        response.raise_for_status()
 
        data = json.loads(response.text)
        data = data['rest']
        ramen_json += data
 
    # 取得したラーメン店のJSONデータを保存
    with codecs.open('ramen_guru' + pref_num + '.json', 'w', 'utf-8') as obj:
        dump = json.dumps(ramen_json, indent=4, ensure_ascii=False)
        obj.write(dump)
定休日を抽出する

HOT PEPPERぐるなびともに定休日がそれぞれ close や holiday としてキー登録されているので定休日の抽出は非常に簡単だ。
また、ラーメン専門店を抽出できているかを確認するため、店舗業態も抽出した。都道府県毎の傾向を見ようと思い都道府県も抽出したが、今回はそこまでやらなかった。
ぐるなびの定休日情報は細かく書かれており、たとえば「毎週月曜日、*元日は休み」など備考が含まれている。今回は定期的な休業日を知りたかったので備考を省くように処理した。
ちなみに、ぐるなびの店舗形態を確認すると「こだわりラーメン」に分類されているラーメン店があった。気になって調べてみると、北海道を拠点する山岡家が用いている業態名称である。チェーン店なのにこだわりラーメンと銘打つのは強気すぎないか。

結果を、1列目に都道府県、2列目に店舗業態、3列目に定休日となるCSVファイルとして保存した。
ぐるなび都道府県毎にCSVファイルを出力した後に結合したファイルを作成した。
ちなみに、CSVファイルを結合する時にもエンコードの問題が発生。結合したファイルを出力する際に encoding='utf-8' と指定しているにも拘わらず、なぜかJIS形式で保存されるため、UTF-8で開こうとして文字化けした。結合するファイルを開く際に、engine='python' と指定することで文字化けを回避した。

# HOT PEPPERとぐるなびの店舗データから定休日を抽出しCSVとして出力
import os
import glob
import json
import codecs
import csv
 
def holiday_pickup_hot(path):
    # HOT PEPPERのJSONデータを処理する関数
    # 指定したpath内のJSONファイルを取得
    # 地域、店舗ジャンル、定休日を抽出
    # CSV形式で保存
 
    # フォルダ中のパスを取得
    All_Files = glob.glob('{}*.json'.format(path))
 
    for file in All_Files:
        #JSON ファイルを開く
        with codecs.open(file, 'r', 'utf-8') as obj:
            df = json.load(obj)
 
        # 地域、店舗ジャンル、定休日を抽出する
        ramen_csv = []
        for i in range(len(df)):
            holiday_list = []
            area = df[i]['large_area']['name']
            genre =  df[i]['genre']['name']
            holiday = df[i]['close']
            holiday_list = [area, genre, holiday]
            ramen_csv.append(holiday_list)
 
        # 抽出した地域、店舗ジャンル、定休日のデータをCSV形式で保存
        name = file.rstrip('.json')
        with open(name + '_holiday.csv', 'w') as obj:
            writer = csv.writer(obj, lineterminator='\n')
            writer.writerows(ramen_csv)
 
def holiday_pickup_guru(path):
    # ぐるなびのJSONデータを処理する関数
    # 指定したpath内のJSONファイルを取得
    # 地域、店舗ジャンル、定休日を抽出
    # CSV形式で保存
 
    # フォルダ中のパスを取得
    All_Files = glob.glob('{}*.json'.format(path))
 
    for file in All_Files:
        #JSON ファイルを開く
        with codecs.open(file, 'r', 'utf-8') as obj:
            df = json.load(obj)
 
        # 地域、店舗ジャンル、定休日を抽出する
        ramen_csv = []
        for i in range(len(df)):
            holiday_list = []
            area = df[i]['category']
            genre =  df[i]['code']['prefname']
            holiday = repr(df[i]['holiday']) #repr()
            holiday = holiday.split(r'\n')
            holiday = holiday[0].split("'")
            holiday_list = [area, genre, holiday[1]]
            ramen_csv.append(holiday_list)
 
        # 取得した地域、店舗ジャンル、定休日のデータをCSV形式で保存
        name = file.rstrip('.json')
        with open(name + '_holiday.csv', 'w') as obj:
            writer = csv.writer(obj, lineterminator='\n')
            writer.writerows(ramen_csv)
 
 
holiday_pickup_hot(path='./ramen_hot/')
holiday_pickup_guru(path='./ramen_guru/')
# フォルダ内にあるCSVファイルを結合し出力
import os
import glob
import csv
import pandas as pd
 
# フォルダ中のパスを取得
DATA_PATH = "./ramen_guru/"
All_Files = glob.glob('{}*.csv'.format(DATA_PATH))
 
# フォルダ中の全csvをマージ
list = []
for file in All_Files:
    list.append(pd.read_csv(file, engine='python', header=None))
 
df = pd.concat(list, sort=False)
 
# 結合しcsvを出力
df.to_csv('ramen_guruPREF01to47_holiday.csv', encoding='utf-8')
定休日を集計する

EXCELでもできそうだが、折角なのでPythonを使って集計した。CSVファイルの3列目を読み込み、定休日をキーとする辞書形式でカウントした。定休日と頻出数のセットとしたリストを作り、それをソートした後に、結果をCSVファイルとした出力した。
曜日は意図通りに集計されるが、「無」、「無休」などは別々に集計される。また、週に二日以上定休日がある場合も、異なるキーとなる。そのため、出力されたデータを手動で再集計した。集計方法には工夫の余地があるものの、結果としては大きな差異はないと思われる。

ぐるなびは定休日が記載されていない店舗が多かった。約20,000店舗の情報を取得したが、その内約16,000件は定休日の情報が「記載無し」であった。これは「null」や「nan」の意味での「記載無し」である。定休日がなく年中無休なのか、そもそも情報が記載されていないのか判断できない。「記載無し」の店舗は省き、残った4,000店舗から定休日を集計した。

# CSVファイル内の指定した列にある単語の出現数をカウントする
import pandas as pd
 
def counter(file_name, read_column):
    # CSVファイルの指定した列の単語の出現数をカウントする関数
    # file_name でCSVファイルを指定
    # read_column で列を指定
 
    df =  pd.read_csv(file_name, engine='python', header=None)
    data = df.iloc[:, read_column]
 
    # 単語を辞書のキーとしてカウントする
    words = {}
    for word in data:
        words[word] = words.get(word, 0) + 1
 
    # 単語と出現数をタプルとしてリスト化しソートする
    dic = [(v,k) for k,v in words.items()]
    dic.sort()
    dic.reverse()
 
    # 結果をCSV形式で保存
    # utf-8 を指定しない
    with open(file_name.strip('.csv') + '_count.csv', 'w') as obj:
        for count, word in dic:
            obj.write(str(count) + ',' + str(word) + '\n')
 
counter(file_name='ramen_holiday_hot.csv', read_column=2)
counter(file_name='ramen_guruPREF01to47_en.csv', read_column=2)

APIを叩いて集計してみた感想

ブログに内容をまとめるのに、コードを書いて結果を得るまでと同じくらいの労力がかかってしまった。

APIそのものは簡単に取り扱えるので、欲しい情報を絞り込むのに検索力が必要に感じた。データを取得でにさえすれば、集計自体も難しくない。エンコードには苦しめられたが……。
最終的に人力で集計した部分もあるが、今回はそこを自動化するのに労力かかりそうだったので、自動化するメリーはないだろう。より多くのデータを集計するならより自動化をすべきだろう。自然言語系の処理は面倒そうだ。

コードを他人に見せることを意識すると、コメントやプログラミングのロジックなどを他の人が読んでもわかりやすいようにしようと自然に思えたので、勉強に向いてるなと感じた。

みんなもゲームのRTA見よう!

YouTubeニコニコ動画でゲームのRTAを見ています。2019年に入り、ゼルダの伝説のブレスオブザワイルドのRTAが30分を切り、スーパーマリオオデッセイのRTAは1時間を切りそうな状況が続いています。共に、1時間弱で楽しめるコンテンツなので見所を紹介していきます。

とうとう30分を切ったブレスオブザワイルド

  • Amiiboあり
    • rasenurns
    • 29m 46s
  • Amiibo 無し
    • sketodara01417
    • 31m 01s 160ms

2019年1月2日、ついにゼルダの伝説ブレスオブザワイルドのRTAAmiibo有りで30分を切りました。2018年の3月に40分台を切ってから実に9ヶ月後の達成です。Amiiboなしも30分台が目前となっています。

ブレスオブザワイルドというか、ゼルダの伝説RTAにはバグ技(glitch)がつきものです。時のオカリナは様々なバグやそれを実行するためのセットアップが多く、そのためRTAのレギュレーションも細かく分かれています。一例として 【RTA】ゼルダの伝説時のオカリナ All Dungeons(オールダンジョン)in 1:18:46【Part1】 - ニコニコ動画 を紹介します。
ブレスオブザワイルドでは、着地時のダメージをキャンセルするバグ技や、シールドによる小ジャンプなどが駆使されてきました。ビタロックで停止させたオブジェクトで移動する「stasis launch」はバグ技というか、仕様でしょう。そのオブジェクトにぶつかってパラセールのスピードをかせぐのはバグ技になりますが。
30分台までの’RTAとしては、【RTA】ゼルダの伝説 ブレスオブザワイルド Any% 40:58 Part1【字幕解説】 - ニコニコ動画 が解説もわかりやすくおすすめです。

ブレスオブザワイルドのRTAにおける30分切りには、2018年後半に発見された二つのバグ技が大きく貢献しています。

一つは、2018年8月末に発見された、縦サーフィンで壁を抜ける「Shield Skew Clip」。壁をすり抜けることで、はじまり塔を起動することなく、祠に侵入できるようになりました。これにより、回生の祠からの脱出、はじまりの塔の起動や、そこまでのイベントを諸々すっ飛ばせます。以前は、回生の祠でゼルダ姫がリンクに語りかけるシーンがそれなりの尺であったため、セリフ読み上げの速いドイツ語でのプレイが推奨されてきました。現在は、ゼルダ姫が語りかけるシーンが減少したため、ドイツ語以外でのタイムロスの影響は軽微になっています。ただし、ロードの関係から Wii U 版の方が早く、RTA勢は Wii U 版でプレイしています。

もう一つが、2018年10月に発見された、敵に吹っ飛ばしもらって超高速で移動する「Bullet time Bounce(BtB)」。これにより、従来から利用されてきた「stasis launch」よりも速く移動できるようになりました。あまりも超高速で移動するため、オブジェクトのロードが間に合いません。使用できる場所は限られますが、祠の扉がロードされる前に侵入する、なんてことも可能です。「BtB」によるぶっ飛びでも、はじまりの大地ではパラセールを取得していないため落下時のダメージをキャンセルする技も健在です。

現在の世界記録保持者であるrasenurns氏は「Shield Skew Clip」と「BtB」の精度を高めることで30分切りを達成しました。ガノン戦はあまり得意ではないようで、その辺が最適化されると28分台も可能かも知れません。ただし「Shield Skew Clip」と「BtB」しっかりとキメないとガノン戦すら辿り着けないため、そんな簡単な話ではありませんけど。

Amiiboを使うと攻撃力アップ食材を入手できたり、金属の箱を呼び出せるため、使用しない場合よりもおよそ30秒ほど早いと言われています。現在のsketodara01417氏によるAmiiboなしの記録は、ギリギリ31分を切れませんでしたが、31分切りは可能でしょう。また、rasenurns氏と比較すると「BtB」による祠間の移動に若干のロスがあるため、恐らくAmiiboなしでも30分は切れそうです。

さらなる更新は、新たなバグ技が登場しないと無理でしょう。ブレスオブザワイルドが発売された当初から、パラセール取得前にはじまりの台地を抜け出す方法が模索されていますが、ガノン討伐につなげられる抜け道は見出されていません。パラセールを手に入れない限り、はじまりの台地以外には地面判定が出現しないようで、仮にガノンまで辿り着けても討伐はできません。最近のゲームにおいて、メモリ書き換え等のバグ技が発見される可能性は非常に低いです。リンクのさらなる高速移動、あるいは敵に大ダメージを与える方法などが発見されないかぎり、大幅な更新はないでしょう。

スーパーマリオオデッセイにおける1時間の壁

スーパーマリオオデッセイのRTAは1時間切り目前です。現在の世界記録保持者である、Chaospringle氏が1時間0分台に突入してから徐々に記録が縮められていますが、なかなか超えられない状況が続いています。最適な動きを続けられれば1時間切りも可能でしょうが、20秒以上の更新には、新たなルートの発見が必須でしょう。1時間00分台への到達は「壁抜け」が大きく貢献しています。壁抜けによる新ルートの発見も模索されているので、1時間切りは新規ルートによってなされるでしょう。

1時間1分台までは、NicroVeda氏が記録を更新しづけていましたが、壁抜けを利用した新ルートになってからは振るっていないようです。現在のルートではChaospringle氏が1時間切りに一番近いプレイヤーではないでしょうか。

マリオオデッセイは、ステージ毎に所定の数のムーンを集めることで、次のステージへ進めます。最終面に到達するために必要なムーンの総数も決まっています。つまり、ステージをすっ飛ばすことはできないため、大幅な更新はなさそうです。
RTAとしては、短時間で取得できるムーンを効率的なルートで集める必要があります。テンポ良くムーンが取得されていく様はRTAの見所です。基本的には、マップをぐるっと一周してワープ無しでスタート地点に戻ってくるのが最適なルートとなります。ただし、特定のイベントをこなすとスタート地点に戻ってくることもあるので、そちらを利用した方が早いステージもあります。
特定のムーンは取得時にムービーが流れるため避けて通りたいところですが、ゲーム進行に必須なケースがほとんどです。ただし、「最初のムーン」など幾つかのムーンを取得せずに進めるようになりました。

その他のRTA動画とか。

普段は、ニコニコ動画の解説付きRTA動画を見ています。多くのRTAは極まりすぎているので、プレイ済みのゲームであっても、予備知識がないと見所がさっぱりわからないのですよね。その点で、解説動画はうれしいです。

【ロマサガ2 RTA 37:46】SFC実機サブフレームリセット技利用【VOICEROID実況】 - ニコニコ動画 などは、解説を見てもロマサガ2RTAおよび、サブフレームリセットを含むバグ技に精通していないとさっぱりですね。
バグ技を駆使するとある種の儀式めいてきます。最終的に「お祈り」が必要となるのも儀式めいてきます。【バグありRTA】METALMAX2 20:54.00 - ニコニコ動画 などは儀式がすぎます。RTA中に他のゲームのRTA動画を流すってどうなの。バグの詳細は 【TAS】メタルマックス2 3:05【更新】 - ニコニコ動画 なども参考にすると、なんとなく理解できるかと思いますが、それでも意味不明です。
ちなみに、祈祷力に関しては 【ゆっくり】バーニングファイトRTA デューク 19分51秒39 【更新】 - ニコニコ動画 がおすすめ。バーニングファイトに関してはRTA動画ではありませんが、【ゆっくり】バーニングファイト ダフィーバグ考察 - ニコニコ動画 はもっと評価されるべき。

栗城さんの死に対してNHKが無自覚すぎる

2019年1月14日にNHKで、2018年5月21日にエベレストで亡くなった栗城史多さんの追悼番組をやっていたが、NHKがあまりにも無自覚で、ひどい構成であった。

番組では、インターネット上の匿名の批判や期待するファンの声によって、栗城さんが追い込まれたのではないか、と締めくくっていた。そこには、NHKを含むメディアやスポンサーの視点が感じられなかった。NHKは彼をしばし好意的かつ無批判に取り上げてきた。そして、今回の番組でも彼に近しい立場での映像がまとめあげられている。つまり、NHKも彼の支援者である。インターネットの声が彼を追い詰めたのならば、NHKも共犯者であろう。しかし、番組においてNHKはずっと傍観者の立場であり続け、メディアやスポンサーへの内省はまったくみられなかった。

栗城さんの最期の挑戦は無謀というより他にない。エベレストの北壁ルートは非常に難易度が高いとされる。それなのに、さらに難易度の高い単独無酸素に挑んでいた。しかも栗城さんは2012年のシシャパンマ挑戦の際に指に凍傷を追い、翌年の2013年にはその影響で9指を切断している。これを無謀といわず、なんというのか。

栗城さんの登山レベルに関しては、登山ライターである森山憲一氏の文春の記事が的確だろう。森山氏は栗城さんに直接会って、彼の挑戦が如何に無謀かを説いたそうだが、物別れに終わっている。

2010年のインタビューから読み解くと、栗城さんは挑戦が無謀であると説かれるほど燃えるタイプであったようだ。最初の挑戦となるマッキンリーも、山岳部の先輩から何度も諭されている。運良くマッキンリーの登頂に成功したことで、栗城さんは無理だとされる挑戦に打ち勝てるイメージを自分の内にも外にも確立していったように思われる。

2007年頃から、ヒマラヤへの挑戦を目指している。ちょうどその頃、電波少年などで知られる土屋プロデューサーが栗城さんを見出した。ニートでも引きこもりでもないのに、「ニートアルピニスト、初めてのヒマラヤ」なる企画名をつけたのは土屋プロデューサーである。この企画を経ることで、栗城さんは全国的に名が知られるようになった。この企画においてチョ・オユーに登頂したことで、支援者やスポンサーが得られやすくなったであろう。


栗城さんが単独無酸素に拘った理由は、彼の掲げる「冒険の共有」にあるのかもしれない。エベレストであっても、十分な支援を受けてのノーマルルートの登頂は可能であっただろう。ただし、ノーマルルートを目指す人は非常に多い。絵面としては「挑戦感」が薄くなってしまう。誰でも登れる感は、栗城さん自身も支援者も望むものではないだろう。「無酸素」に拘ったのも、実況するためだったのだろうと思われる。指を失ったのも「冒険の共有」のためにカメラを操作するために手袋を外したからだとも言われている。「冒険の共有」のための演出が、彼を無謀な挑戦に向けていたように思われる。

先の森山氏の記事にもあるように2015年以降はその「冒険の共有」もうまく行っていなかったようだ。NHKの番組で流れた、亡くなる直前の栗城さんが、キャンプ地でスタッフに激昂している映像は、彼自身が追い詰められている様に見えた。
栗城さんは指を失った2012年からしばらく挑戦をしていない。2015年に挑戦を再開してからは、森山氏が指摘するようにアンコントロールな状態にあったように思われる。このような状態に至ったのはNHKが指摘するようにインターネットにおける批判も一因だろう。先に挙げたように、栗城さんは批判されればされるほど、無理な挑戦を目指すタイプであるし、支援者やスポンサーはそのような彼に期待をしていた。また「冒険の共有」を標榜する以上、インターネットからの応援も、番組が指摘するように、栗城さんを追い詰めたとも想像できる。
番組は、インターネットの批判者が、あるいはファンが彼を追い詰めたという論調であった。しかし、彼を物理的にかつ金銭的に支援するスポンサーがいなければ、彼は挑戦を続けられない。彼自身が、テレビ的な誇張により無謀とされる困難に挑戦することで自身をプロデュースしていたし、そして周囲もそれを求めていた。そのような彼を後押したのはメディアである。特に、NHKは彼を何度か特集している。これにより、支援者やスポンサーが得やすくなった側面はあるだろう。そのようなNHKが自己を内省することなく、栗城さんの死はインターネットの闇であるかのような構成の番組を放映することが信じられない。


森山氏のように、彼に直接忠告する人はいたが、その声は彼には届かなかったし、むしろ彼を奮い立たせるだけだったのかもしれない。指を失って再挑戦してからは、引くに引けない状況になっていたように思える。
多くの支援者の協力を受けて、単独無酸素でない道もあったのではないかと思う。「冒険の共有」としては、その方が正しいとも思える。しかし、彼が確立していった挑戦者というスタイルが、それを許さなかったのだろう。
インターネットの声が彼を追い詰めたとするならば、メディアや支援者、そしてスポンサーも彼を死に追いやって点を明言するべきであったと思う。

追記 2019年1月17日

ブックマークのコメントにて紹介されていた、かつて栗城さんに同行しドキュメンタリーを制作した方のブログを紹介しておく。
自分の「夢」を人に語るな | チェ・キタラの「隅っこまで照らすな!」