グランディ数

Competitive Programming Advent Calendar 2016 - Adventarの12日の記事になります。

この記事では、競技プログラミングの問題にたまに出てくる、グランディ数についての解説記事です。解説と言いつつ、ひたすら自己流の証明を書きなぐった記事みたいになってしまったので、読みづらいかもしれません・・・。内容としてはグランディ数の基礎的な部分しか扱っていないので、知っている人は読む必要がないかもしれません。

グランディ数を知ると何ができる?

競技プログラミングでたまに見かける、2人で交互に行う系のゲームの勝敗判定を行う問題の解法によくグランディ数が使われます。実装自体はとても簡単なので、知らないと全くわからないが、知っていると超簡単になる傾向があります。なので知っていて損ではないと思います。グランディ数自体はとても面白いので、少し興味があれば知っておくといいと思います。

Nimゲームの必勝法

グランディ数の考え方の大元には、Nimゲームの必勝法が使われます。まずはじめにNimゲームの必勝法について見ていきましょう。Nimゲームとは、以下のようなゲームです(Wikipediaから引用)。

2人のプレイヤーがいくつかのコインの山からコインを取り合う。ゲームの目的は、最後のコインを取ることである。(コインの代わりに石や豆を使ってもよい)。

  • まず、コインの山を複数準備する。山の数や各山のコインの枚数は(プレイヤー同士で合意すれば)自由に決めてよい。
  • 2人のプレイヤーの順番は交互である。じゃんけん等で先手を決める。
  • 各々のプレイヤーは自分の順番のとき、コインの山を1つ選んでその山から1枚以上のコインを取り去る。(複数の山からコインを取ったり、コインを一枚も取らなかったりするのは反則である)。これが終わったら次のプレイヤーの手番になる。
  • すべての山のコインがなくなったとき、ゲームは終わりである。そして、最後のコイン(複数のときもある)を取ったときのプレイヤーが勝者である。
https://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%A0

ここで、山の数をnとし、i番目の山のコインの枚数をs_iとします。このゲームの勝敗判定は、各山のコインの枚数の排他的論理和(XOR)で判定できます。

\displaystyle G(s) = s_1 \oplus \cdots \oplus s_n

G(s)の値から、この山の状態sからコインを取ろうとしているプレイヤーの勝敗がわかります。G(s) = 0のときは負け、それ以外のときは勝ちとなります。

ここまでは誰でもできます。人によっては常識レベルかもしれません。しかし、そもそもなぜこれで勝敗判定ができるのでしょうか?また、勝ちと判定された場合、プレイヤーはどのようにコインを取れば良いのでしょうか?

必勝法を考える

まず、シンプルなn = 2(山が2つ)の場合を考えましょう。1番目の山のコインの枚数がx、2番目の山のコインの枚数がy枚という状態を{x, y}と書くことにします。先程の勝敗判定の式から、{7, 8}は勝ち、{8, 8}は負けとなります。この場合には有名な必勝法があります。それは、2つの山のコインの枚数が同じになるようにコインを取るというものです。{7, 8}の場合で試すと、

{7, 8} -> {7, 7} -> {3, 7} -> {3, 3} -> {3, 0} -> {0, 0}

のように、絶対に先手が勝つことができます。この遷移の各状態に、先程の勝敗判定の式を適用してG(s)の値を計算してみましょう。

15 -> 0 -> 4 -> 0 -> 3 -> 0
(7 \oplus 8 = 15, \space 7 \oplus 7 = 0, \space 3 \oplus 7 = 4, \space 3 \oplus 3 = 0, \space 3 \oplus 0 = 3, \space 0 \oplus 0 = 0)

このように、0とそうでない数字が交互に並んでいます。つまり、必勝法というのは、相手にG(s) = 0の状態を与える手を選択し続けることなのです。G(s) = 0の状態からどのようにコインを取っても、必ず次の状態 s' G(s') \ne 0となるので、負けの人は為す術がありません。

山が2つの時の必勝法がわかりましたが、実はこの必勝法は山の数がいくつでも成り立ちます。山が沢山ある場合でも、勝ちの人は常に相手の手番のG(s)の値が0となるようにコインを取ればよいのです。

必勝法が成り立つための2つの条件

ここまで当然のように書きましたが、2つの疑問が浮かび上がると思います。

  1. G(s) = 0である状態sの、任意の次の状態s'は必ずG(s') \ne 0となるのか?
  2. G(s) \ne 0である状態sからG(s') = 0である次の状態s'へ遷移できる手は必ず存在するのか?

1つ目は負けの人の手、2つ目は勝ちの人の手に関係します。そもそもこれが成り立たないと先程書いた必勝法は成り立ちません。それぞれ証明してみましょう。

G(s) = 0である状態sの、任意の次の状態s'は必ずG(s') \ne 0となるのことの証明

これは簡単です。1回の手では、s_1s_nから1つを選んで、その値を1以上減らします。数字が変化している以上、1箇所以上のビットが変化しているはずです。ですので、G(s')の値は必ずG(s)の値と異なります。G(s) = 0ですので、G(s') \ne 0が成り立ちます。

G(s) \ne 0である状態sからG(s') = 0である次の状態s'へ遷移できる手は必ず存在することの証明

G(s)の値で1となっているビットのうち、最上位のものに着目します。これと同じ位置のビットが立っているs_iが存在するはずなので、そのうちの1つをs_kとします。この値を減らすことで、最上位のビットが1から0になり、かつそれ未満の位置に任意のビット列を含む数字に変化させことができます。このビット列を、G(s)の立っているビットを打ち消すように選択することにより、G(s') = 0とすることができます。

s = \{8, 6, 5\}の例を見てみましょう。わかりやすさのため、2進数表記で表します。

s_1 = 8_{(10)} = 1000_{(2)}
s_2 = 6_{(10)} = 0110_{(2)}
s_3 = 5_{(10)} = 0101_{(2)}
G(s) = 1011_{(2)}

G(s)の右から数えて4番目のビットが最上位の立っているビットです。それと同じ位置のビットが立っているのはs_1で、その値は8です。この数字を減らすことで、0000_{(2)}0111_{(2)}の任意の数字を作り出す事ができます。G(s) = 1011_{(2)}の、011のビット列に合わせて、0011_{(2)}を作りましょう。この値は10進数では3なので、8から5を引けば作れます。その結果を見てみましょう。

s'_1 = 3_{(10)} = 0011_{(2)}
s'_2 = 6_{(10)} = 0110_{(2)}
s'_3 = 5_{(10)} = 0101_{(2)}
G(s') = 0000_{(2)}

めでたくG(s') = 0にすることができました。

Nimゲームの必勝法まとめ

Nimゲームの必勝法について見ていきました。山の数をnとし、i番目の山のコインの枚数をs_iとしたとき、次のことが言えることがわかりました。

  • 自分の手番の山の状態がsであるプレイヤーの勝敗は、s排他的論理和(G(s) = s_1 \oplus \cdots \oplus s_n)で判定でき、この値が0の場合は負け、それ以外の場合は勝ちとなる。
  • 勝ちの人の必勝法は、相手の手番の状態s'G(s') = 0となる手を選び続ければ良い。

次は、この知識を使ってグランディ数について見ていきましょう。

グランディ数が計算できればNimゲームの必勝法が適用できる

グランディ数はNimberとも呼ばれています。その名の通り、Nimゲームが関係します。次のゲームの必勝法を考えてみましょう。

板チョコがn枚あり、i番目の板チョコはh_i \times w_iピースで構成される。これらの板チョコを2人交互に食べていく。自分の手番の時、板チョコを1枚選び、それ1枚をすべて食べるか、縦か横の切れ目に沿って2枚に分割してどちらか片方を食べる。最後のチョコを食べた人が勝ちとなる。

Nimゲームの山がコインから板チョコに変化したゲームです。このゲームにも必勝法があります(必勝法があるかどうかの判断方法も書いたほうがよい?)。実は、各板チョコのグランディ数を求めることにより、Nimゲームと全く同じ必勝法を適用することができます。簡単に言うと、「グランディ数=山のコインの枚数」とするNimゲームに帰着できるということです。

グランディ数の求め方

グランディ数は、ゲームの全体の状態(複数の板チョコ)ではなく、プレイヤーが自分の手番で変更を加えることができる単体の状態(1枚の板チョコ)に視点を当てて計算するのが基本です。

まず、負けの状態(その板チョコに対して操作が行えない)のグランディ数を0とします。ここでは、0 \times 0の板チョコのグランディ数を0とします。それ以外の状態sのグランディ数は、以下の方法で求めます。

  1. 状態sから1回で遷移できる任意の状態s'のグランディ数を求める。このグランディ数の集合をVとする。
  2. Vに含まれない最小の非負の整数を、状態sのグランディ数とする。

これは、グランディ数gの状態は、1回の操作でグランディ数g-10の状態に遷移できることを意味します。Nimゲームにおいて、x枚のコインの山は、1回の操作でx-10枚に変化させることができます。つまり、このようにグランディ数を定めることで、「グランディ数=山のコインの枚数」とするNimゲームに帰着できるというわけです。

例として、3 \times 2の板チョコのグランディ数を求めてみましょう。板チョコの状態を(縦のサイズ, 横のサイズ)と表すことにします。まず、各状態が1回で遷移できる状態を列挙しましょう。例えば、(3, 2)は1回の操作で(3, 1), (2, 2), (1, 2), (0, 0)の4つの状態に遷移可能です。

(3, 2) -> (3, 1) or (2, 2) or (1, 2) or (0, 0)
(3, 1) -> (2, 1) or (1, 1) or (0, 0)
(2, 1) -> (1, 1) or (0, 0)
(1, 1) -> (0, 0)
(2, 2) -> (2, 1) or (1, 2) or (0, 0)
(1, 2) -> (1, 1) or (0, 0)

(0, 0)のグランディ数として0を当てはめ、順番にグランディ数を計算しましょう。ここで、g(h, w)は板チョコの状態(h, w)のグランディ数を表し、mex(S)は、集合Sに含まれない最小の非負の整数を表します。

g(0, 0) = 0
g(1, 1) = mex(\{g(0, 0)\}) = mex(\{0\}) = 1
g(1, 2) = mex(\{g(1, 1), g(0, 0)\}) = mex(\{1, 0\}) = 2
g(2, 1) = mex(\{g(1, 1), g(0, 0)\}) = mex(\{1, 0\}) = 2
g(2, 2) = mex(\{g(2, 1), g(1, 2), g(0, 0)\}) = mex(\{2, 2, 0\}) = 1
g(3, 1) = mex(\{g(2, 1), g(1, 1), g(0, 0)\}) = mex(\{2, 1, 0\}) = 3
g(3, 2) = mex(\{g(3, 1), g(2, 2), g(1, 2), g(0, 0)\}) = mex(\{3, 1, 2, 0\}) = 4

めだたくグランディ数を計算することができました。これであとはNimゲームと同じ必勝法を適用したいところですが、ちょっと待って下さい。g(2, 2)について見ると、

g(2, 2) = mex(\{g(2, 1), g(1, 2), g(0, 0)\}) = mex(\{2, 2, 0\}) = 1

グランディ数が1にもかかわらず、グランディ数が2の状態に遷移できています。これは、Nimゲームに置き換えると、コインが1枚から2枚に増える操作を許していることになります。これではNimゲームではないじゃないかと思うかもしれません。しかし、重要なのは、Nimゲームで使用した必勝法(相手の手番の状態s'G(s') = 0となる手を選び続ける)が適用できるかどうかです。先程と同様に、必勝法が成り立つための2つの条件について考えてみましょう。

G(s) = 0である状態sの、任意の次の状態s'は必ずG(s') \ne 0となることの証明

グランディ数の定義から、ある状態のグランディ数と、そこから遷移した状態のグランディ数は必ず異なります。よって、Nimゲームの時と同様にこれは成り立ちます。

G(s) \ne 0である状態sからG(s') = 0である次の状態s'へ遷移できる手は必ず存在することの証明

Nimゲームの時と同じ方法をとれば、G(s') = 0へ遷移できます。

これで、今度こそ安心してNimゲームの必勝法を適用することができます。各板チョコのグランディ数を求め、排他的論理和をとることで、勝敗判定をすることができます。

以下は、1枚の板チョコの縦横のサイズを入力すると、その板チョコのグランディ数を求めてくれるプログラムです。グランディ数はメモ化再帰で求めることが多いです。

#include <iostream>
#include <cstring>
#include <set>
using namespace std;

const int MAX_H = 100;
const int MAX_W = 100;
int grundy[MAX_H][MAX_W];

int calcGrundy(int h, int w)
{
  int &g = grundy[h][w];
  if (g != -1) return g;
  if (h == 0 && w == 0) return g = 0;
  set<int> V;
  V.insert(calcGrundy(0, 0));
  for (int i = 1; i < h; ++i)
    V.insert(calcGrundy(i, w));
  for (int i = 1; i < w; ++i)
    V.insert(calcGrundy(h, i));
  g = 0;
  while (V.count(g)) ++g;
  return g;
}

int main()
{
  memset(grundy, -1, sizeof(grundy));
  int h, w;
  cin >> h >> w;
  cout << "Grundy number of (" << h << ", " << w << ") is "
       << calcGrundy(h, w) << endl;
  return 0;
}

分割が起こるゲームにおけるグランディ数

今度は次のゲームの必勝法を考えてみましょう。

板チョコがn枚あり、i番目の板チョコはh_i \times w_iピースで構成される。これらの板チョコを2人交互に分割していく。自分の手番の時、1 \times 1以外の板チョコを1枚選び、それ1枚を縦か横の切れ目に沿って2枚に分割する。分割できなくなった人の負けとなる。

先程のゲームと似ていますが、このゲームでは板チョコの「分割」が起こります。今まで登場したゲームでは、複数あるもののうちから1つを選び、その状態を変更するものでした。しかし、今回のゲームでは、複数あるもののうちから1つを選び、それを複数に分割します。このようなゲームでも、グランディ数を計算することができるのでしょうか?

実は、以下の定理を使うことで、この問題を解くことができます。

g(s, t) = g(s) \oplus g(t)

これは、2つの独立したゲームs, tを状態としてもつゲームのグランディ数はs, tそれぞれのグランディ数の排他的論理和に一致する、ということです(この定理の証明をすごく入れたかったのですが、思ってたより重いので省略しますすいません)。この定理を繰り返し適用することで、3つ以上の独立したゲームについても同様のことが成り立ちます。

この定理を使うことで、複数に分割された状態を1つのグランディ数で表現することができ、先ほどと同様の解法が適用できます。今回の板チョコの問題だと、1回の操作で2枚の板チョコs, tに分割された時、その状態のグランディ数はg(s) \oplus g(t)と表現することができます。

プログラムで書くと以下のようになります。遷移先の状態で排他的論理和をとるようになった以外は先ほどと同じです。

#include <iostream>
#include <cstring>
#include <set>
using namespace std;

const int MAX_H = 100;
const int MAX_W = 100;
int grundy[MAX_H][MAX_W];

int calcGrundy(int h, int w)
{
  int &g = grundy[h][w];
  if (g != -1) return g;
  if (h == 1 && w == 1) return g = 0;
  set<int> V;
  V.insert(calcGrundy(0, 0));
  for (int i = 1; i < h; ++i)
    V.insert(calcGrundy(i, w) ^ calcGrundy(h - i, w));
  for (int i = 1; i < w; ++i)
    V.insert(calcGrundy(h, i) ^ calcGrundy(h, w - i));
  g = 0;
  while (V.count(g)) ++g;
  return g;
}

int main()
{
  memset(grundy, -1, sizeof(grundy));
  int h, w;
  cin >> h >> w;
  cout << "Grundy number of (" << h << ", " << w << ") is "
       << calcGrundy(h, w) << endl;
  return 0;
}

今回は2つに分割される場合でしたが、3つに分割される場合、nつに分割される場合でも同様に排他的論理和をとることで1つのグランディ数で表現することができます。この定理から、Nimゲームの時に出てきたG(s)は、ゲーム全体の状態を表すグランディ数と捉えることもできます。

グランディ数まとめ

ここまで、グランディ数について見てきました。しかし、実際にコンテストでグランディ数を扱う場合、証明などの難しいことは一切考える必要がありません。グランディ数自体が何なのかわからなくても、以下のグランディ数の求め方さえ理解していれば、問題を解くことができます。

  • 負けの状態のグランディ数を0とし、それ以外の状態sのグランディ数は、以下の方法で求める。
    1. 状態sから1回で遷移できる任意の状態s'のグランディ数を求める。このグランディ数の集合をVとする。
    2. Vに含まれない最小の非負の整数を、状態sのグランディ数とする。
  • 1回の遷移で分割が起こる場合、分割された各状態のグランディ数の排他的論理和をとれば良い。

この記事は以上になります。ありがとうございました。

ACM-ICPC WF 2016 5日目(5月19日)

今日がいよいよWorld Finals本番である。コンテスト開始まで昨日とほぼおなじなので割愛する。

f:id:zukky162:20160529221156j:plain

コンテストが始まって、昨日と同じくさて君にFlymakeとCtrlの設定を書いてもらい、自分と怒髪君で問題を読む。B問題が読みづらい問題文だったが、読解にかなり集中できたので間違わずに読む事ができた。読めただけで解けてはいない。

さて君はFlymakeのタイピングを前日夜に3回やっていたが、不運にもバグってしまう。自分も協力して動くようになった。

さて君がそのままC問題を読み、やるだけとのことなので書いてもらう。問題なくAC。

周りの様子を見て、L問題に取り掛かる。しかし、WA。怒髪君に相談し、合ってそうな貪欲解が返ってきたので書いたらAC。怒髪神。

その後はG問題を考えた。これはどう見ても偏角ソート。しかし、誤差によりWA連発。最終手段としてEPSを外したら何故か通った。

それと平行して怒髪君はK問題を進めていた。怒髪君は真ん中に1を集めればよいという解法を見出していたが本当に合ってるか不安だったので自分が証明したら問題なかった。怒髪神。書いてもらい、少しWAが出てしまったがその後AC。

周りの様子を見て、圧倒的に解かれていたE問題に手を出すが、チームメンバー誰一人わからず。みんな解いているから自分でも解けるはずと思い、ずっと考えていたが解法は降ってこなかった。

自分がE問題を考えている間、怒髪君とさて君はB問題を考えていた。それっぽい解法を思いついていたので書いてもらったが通らず。コンテスト終了までずっと書いていたが解けず。

結果、我々会津大学のチームFinalZukkyは4問解けて128チーム中77位だった。出題される問題がどれも難しいにもかかわらずみんな解いてくるので、世界の広さを実感した。

f:id:zukky162:20160529221436j:plain

サンクトペテルブルクと上海交通の圧倒的2強だったが、サンクトペテルブルク優勝した。

コンテストが終わってからは特にイベントが無かったので、ホテルに帰ってからみんなとわいわい過ごして、翌日帰国した。

 

これで自分のICPC人生は終わりを迎えた。6年間、長いようであっという間であったが、目標であったWorld Finalsへの出場を果たすことが出来て本当によかった。欲を言えばWorld Finalsでもっと良い成績を残したかったが、出場権を失ってしまったので、それは後輩に託すことにする。一緒にチームを組んでくれた方々、そして顧問の渡部先生、今まで本当にありがとうございました。

SO2 -> bOOch -> oshieteZukky -> tanondaZukky -> AizukkYYY -> FinalZukky

 

ACM-ICPC WF 2016 4日目(5月18日)

World Finals本番を明日に迎え、今日はそのリハーサルが行われた。

リハーサルなだけあって今日のスケジュールは明日とほぼ同じになっている。

朝食を済ませ、30分ぐらい車に乗ってコンテスト会場がある建物に移動する。

会場の入り口でアクエリアスをもらった。しかし、ペットボトルの中の液体の色が怪しかったので飲むのをやめた。

コンテストエリアに入るまで時間の余裕があり、その間にBill Poucherさんと写真を撮ってもらった。

f:id:zukky162:20160529220727j:plain

しばらくしてから、コンテストエリアに入場した。トロフィーに触ってから自分たちのチームの席に移動した。自分たちの席は角の方だった。

f:id:zukky162:20160529220734j:plain

席に着いてからリハーサル(プラクティスセッション)が始まった。今回のプラクティスでFAを取ると賞金がもらえるとのことだった。

始めに環境について書く。キーボードは当たり前だがUS配列だった。マウスパッドが2016WF仕様だったが、その上でマウスを動かすとマウスカーソルが明後日の方向に飛んでいってしまうので使わないようにした。今回はオプション付きのコンパイルコマンドと提出用のコマンドと印刷用のコマンドが予め用意されていて便利だった。ただ、提出コマンドは実行しても結果がコンソールに表示されないところが少し不便だった。あと、トイレが謎の形状をしていた(普通の洋式トイレもあった)。

ここからプラクティスセッションの内容を書くが、うろ覚えで実際と異なるかもしれない。さて君にFlymakeの設定とCtrlキーの設定をタイピングしてもらい、その間、自分と怒髪君は問題を読む。見たことのある問題がいくつか並んでいた。どうやら過去のWFの問題らしい。フェリーの問題はさて君が解いていたのでそのままさて君に投げて、自分は確か解いたことがあるクレーンの問題をやることにした。さて君がFlymakeの写経をミスって苦戦していたので、自分はアシストに入った。それが終わってからフェリーの問題を解くことにした。さて君にメイン部分だけを書いてもらい、その間自分は式の計算を紙で行い、それらを組み合わせて提出。確か数回WAからのAC。その後自分はクレーンの問題を書くがバグるので怒髪君にA問題を書いてもらう。怒髪君の問題は普通にAC。その間自分はうろ覚えの多角形の重心計算をチェックしていた。その後もグダグダしていたが、重心のx座標が単調増加or減少であることに気づいて(思い出して)なんとかAC、したはず。最後はAliceとBobのゲーム系問題に取り掛かっていたが解けずに終了。

f:id:zukky162:20160529220730j:plain

リハーサルが終わって、午後も本番環境で練習ができたので少し触ってから、ホテルに帰った。実装力に不安があったので問題を幾つか解いてから就寝した。

 

ACM-ICPC WF 2016 3日目(5月17日)

前日はリゾートを堪能した。今日からWorld Finalsのイベントが始まる。

午前中はホテルでIBMのTeckTrek を聞いた。真面目なトークが行われると思いきや、登壇者がスターウォーズのコスプレをして登場してだいぶ賑やかだった。発表もIBMらしい内容だったが、英語が苦手なので詳しく理解することはできなかった。メンバーはスターウォーズ好きだということはわかった。

f:id:zukky162:20160529214748j:plain

午後はFanta Seaと呼ばれるテーマパークのような場所に移動した。World Finalsのオープニングセレモニーが今日ここで行われる。入場券代わりのシールの粘着力が強力で、ICPCの名札に貼って剥がせない人が続出していた。自分は過去に似たような過ちをしたことがあったので回避できた。前日背中が日に焼けた影響でリュックの肩紐が肩に擦れるだけで痛みが走り、背負いながら歩くのがしんどかった。

f:id:zukky162:20160529215314j:plain

人の流れに付いて行くと、遺跡のような大きな建物に着いた。中に入るまで気づかなかったが、ここがオープニングセレモニーの会場だった。

f:id:zukky162:20160529214853j:plain

オープニングセレモニーはACM-ICPCの関係者の方々のお話を聞いたが、やはり英語力不足で内容をほとんど理解できず、途中寝てしまった。僕のような人を考慮しているのか、合間合間にプーケットの演舞などを挿むプログラム構成となっていた。

f:id:zukky162:20160529214954j:plain

オープニングセレモニーの後は自由時間だった。象がメインのショーが行われると聞いていたので、始まる時間までテーマパーク内のお店を周っていた。輪投げや射的で遊べる店や、お土産を売っている店など、いろいろな店が並んでいた。同期へのお土産にプーケットのお菓子が欲しかったが、残念ながら売っていなかったので何も買わなかった。

f:id:zukky162:20160529215308j:plain

ショーはオープニングセレモニーと同じ遺跡のような建物の中で行われた。象を見るのはこれが初めてで、すごい迫力だった。間近で見ると歩くスピードがかなり早くて驚いた。象だけでなく、演舞やマジック、動物のショーなどもあり、とても楽しめる内容だった。残念ながら撮影禁止だったので写真はない。

ショーが終わった後は近くの広場で象に乗せてもらった。やはり歩く速度がとても速かった。体感自転車ぐらいのスピードはありそうだった。高さも結構あって、備え付けの椅子が象の揺れで落ちないか不安だった。象に乗る貴重な体験ができて大満足だった。

f:id:zukky162:20160529214958j:plain

↑これぐらい速い

夜も遅く、かなり疲れていたので明日に備えてすぐに就寝した。

 

ACM-ICPC WF 2016 2日目(5月16日)

今日は1日中ホテルでのリゾートを堪能して終わった。

我々はまずはじめにホテルのすぐ眼の前にある海に行くことにした。これを予想して自分とさて君は海パンを持ってきていた。白い砂、青い海、という言葉がそのまま当てはまる大変綺麗な海で気分は最高だった。その勢いで海へとダッシュしたが、スタッフに止められる。どうやら入れないらしいが、無視して海へと入った。数分後、止められた理由がわかった。波が強すぎる。リゾート地の海はもっと穏やかな感じを想像していたが、今日の海は凶悪で、人を殺しに来る波だった。

f:id:zukky162:20160529213927j:plain

仕方が無いのでホテルのプールに行くことにした。海ではなかったがこちらも最高だった。2種類あるプールのうちの片方に入ったが、深さが2m近くあり、かなり深かったが30分ぐらい泳いでいたら慣れた。

f:id:zukky162:20160529214202j:plain

プールの後は日本の他大学勢とパターゴルフをした。あまりゴルフはやったことが無かったが、なかなか調子がよかった。最高だった。

f:id:zukky162:20160529220037j:plain

昼食を取りにホテルに戻ると、海の前が気づいたら祭のように賑わっていた。ハンバーガーなどの店が並び、それらすべては(参加者なら)無料で配っていた。最高だった。

f:id:zukky162:20160529214156j:plain

その後もう片方のプールに入った。こちらは深さは普通だった。昨日のレジストレーションで貰ったACMのビーチボールをふくらませて遊んでいた。これが思ってたよりもかなり楽しかった。最高だった。

その後は3on3のバスケをやったが、5分ぐらいで力尽きた。情報系の人間には厳しいスポーツだったが、いい運動ができて最高だった。

その後はアーチェリーをやった。思ったよりも弦を引くのが重かった。初めてでも簡単に真っ直ぐ矢を飛ばすことができて、的に当てることができた。最高だった。

f:id:zukky162:20160529215856j:plain

その後は懲りずにまたプールで遊んだ。最高だった。

その後はボードゲームやカードゲームなどが遊べるコーナーで休んでいると、同じ部屋に置かれたピアノからスマブラのテーマ曲が聴こえてきた。きゅうりさんとさて君のコミュ力により、演奏していた人と話すことができた。ピアノを弾いていた人はやはりスマブラ好きの人だった。そしてそのまま3DSスマブラ会が行われた。最高だった。

f:id:zukky162:20160529215907j:plain

今日はとにかくリゾートを満喫した。本当に最高だった。満喫しすぎたので疲れてしまったのですぐに寝ることにした。

ACM-ICPC WF 2016 1日目(5月15日)

普段はブログを書かない人間だったが、せっかくWorld Finalsに出場出来たので、その参加記を書こうと思う。

 

今日は会場であるプーケットへの移動で一日が終わった。

自分は4月から某D社の人間として出社している社会人となっていた。研修段階の自分がWorld Finalsで1週間ほど休みを貰えるか不安だったが、何の問題もなく、むしろ全力で送り出してもらえた。

そんなわけで、チームメンバーで自分だけ都内に住んでいるので成田空港に集合することにした。

さて君だけは前日に都内の自宅に泊まった。成田に集合が8:45だったので、余裕持たせて30分ぐらい前に到着するスケジュールで成田に移動しようとさて君に提案したが、「早すぎません?」との返答が来たので仕方がなく8:35到着予定のスケジュールにした。案の定遅れた。

成田空港で怒髪君とコーチの渡部先生と合流。卒業して1ヶ月経ったがあまり雰囲気は変わっていなかった。

移動中はQuestをやっていた。Questとは、WF中に開催される主にツイッターを使ったイベントである。お題に関することをつぶやくとそのお題に設定された得点が手に入るというものである。移動に使う飛行機の写真を撮ったり、日本円で175THB(タイバーツ、タイの通貨)を表現したり、象を探したりしていた。

f:id:zukky162:20160529220211j:plain

↑175THB≒536円

飛行機で成田空港→バンコクプーケットの経路で移動した。約9時間の移動はやはり疲れた。前回のシンガポール大会の時の行きの機内でノロウイルスらしき症状が発症して死にそうになったので、今回はそうならないように祈っていたが、何事もなく無事移動が完了して安心した。

f:id:zukky162:20160529220226j:plain

プーケットの空港についてからは車でホテルまで移動した。海外特有の交通ルールガバガバな運転だったが、無事生きてホテルに辿り着くことに成功した。

とにかく豪華なホテルだと感じた。リゾート地なだけはあり、通路からは広いプールが見えた。ビーチも隣接しているらしいので、明日遊びに行ってみようと思う。部屋は広く、でかいベッドがあり、最高だと思った。ただ、バスルームはアジア特有の外からスケスケな仕様だった。

ホテルに着いてからは大会のレジストレーション作業をした。自分だけ卒業してしまったので学生証が無かったが、大丈夫そうだった。レジストレーション作業は10項目ぐらいやることがあったが、各項目ごとにスタッフがいるテーブルを設けていてそこに順番に案内されたので割とすんなり終わった。さすがWFの運営だと思った。World Finalsと書かれたTシャツやノベルティなどをもらってかなりテンションが上がった。

レジストレーションが終わってレストランで夕飯を食べようとしたがすでに終了していた。仕方が無いので非常食として持ってきたカロリーメイトフルーツ味とウイダーinゼリーマルチビタミンを食した。

明日は選手はフリーらしいので、いろいろとエンジョイしようと思う。