D言語のおはなし

※この記事は ICT Advent Calendar 2018 18日目の記事です.

昨日はICUに合格したらしいまなえです.

manaway1019.hatenablog.com

明日は,私が高専を去ることを心底悲しんでいるkurokojiです.

親しい人が離れていくというのは正直寂しいですよ.はい.

毎年私からの愛のこもったフリを受けるために,私の次の日に記事を書いちゃう可愛いやつです.

誤りがあるので言っておくと,僕のほうが先に18日に書く予定を入れていたのに,17日に後からわざと入れたのはあなたでしょ??? 事実を曲げることは良くないです.


D言語を流行らせたい

全人類が学ぶべき言語であるところのD言語.しかし実態はあまり知られていないように思います.

D言語くんは知っていますか?

D言語くん

Twitter等で一度は見たことがあると思います.何かと変なポーズなので,ほとんどの人が笑いの対象としか見ていないような気はしますが.

「変なマスコットキャラクターが存在する,ようわからん言語」

そのイメージを払拭するために,このD言語販促記事を書いてます.

あ,ちなみに今年の高専プロコン競技部門のソルバはD言語で書かれています.

https://github.com/kurokoji/procon29-kyogi/tree/master/nagatogithub.com

軽い文法の紹介

import std.stdio, std.array;

void main() {
  writeln("Hello, World!!");
  for (size_t i = 0; i < 10; ++i) {
    writeln(i);
  }

  /*
  上のfor文と同じ動きをする
  foreachの場合,iの型は何も書かなくても型推論してくれます
  */
  foreach (i; 0 .. 10) {
    writeln(i);
  }

  uint[] ar = [1, 2, 3, 4, 5];
  ar[0 .. 3] = 0; // [0, 0, 0, 4, 5]
  ar[0 .. $] = 6; // [6, 6, 6, 6, 6]
}

import std.stdio はC,C++でいう #include <stdio.h> みたいなもの.厳密には違うけど,まぁそれは置いておいて.

writelnprintf のような標準出力関数です.writeln は改行が最後に勝手に入ります.write なら入りません.

writelnprintf と違って書式文字列("%s %s" みたいな)は使えませんが,writefln で同様に出来ます.

一般的な for も使えますが foreach も使えます.

C,C++などと違って,unsigned intuint です.短くて良い.

で,ar[0 .. 3] = 0 みたいに配列のここから,ここまで0を代入する,みたいなのが一行で分かりやすく書けます.

if などは他の言語と大体一緒です.

面白い機能の紹介

D言語には面白い(しかも便利)機能がたくさんあります.

などなど.

いくつか紹介します.

UFCS(統一関数呼び出し構文)

import std.stdio;

int inc(int a) {
  return a + 1;
}

void main() {
  int n = 1;
  writeln(inc(n)); // 2
  writeln(n.inc()) // 2
}

writeln(inc(n)) は普通ですね. writeln(n.inc()) に注目していただきたい.

UFCSとは関数の第1引数を関数の前に持ってきて,まるでメンバ関数のように振る舞うことが出来る構文です. これによりメソッドチェーンがやりやすくなり,見た目もよりキレイになります.

CTFE(コンパイル時関数実行)

import std.stdio, std.string, std.conv;

bool[] Eratosthenes(ulong N)() {
  bool[] is_prime = new bool[N + 1];
  is_prime[2 .. $] = true;

  for (ulong i = 2; i * i <= N; ++i) {
    if (is_prime[i]) {
      for (ulong j = i * 2; j <= N; j += i) {
        is_prime[j] = false;
      }
    }
  }
  return is_prime;
}


void main() {
  // enumをつけることで,強制的にCTFEを発動させることが出来ます
  enum tmp = Eratosthenes!100000;
  write(">> ");
  auto n = readln.chomp.to!uint;
  writeln(tmp[n]);
}

readln.chomp.to!uint は標準入力から一行文字列を受け取って,改行を取り除き,uint に変換しています.

このプログラムはエラトステネスの篩という素数を列挙するアルゴリズムです. 通常,この計算はN が大きくなると計算が遅くなります.そこで,CTFE(コンパイル時関数実行)を用いると高速化が見込めます.

名前の通り,この機能はコンパイル時に関数を実行,つまり計算を行っています. これにより,実行時は列挙部分の計算は行わないため,高速化が可能です.

ただし,計算結果を埋め込むことになるので吐いたバイナリのサイズは大きくなります.

mixin

void main() {
  mixin("writeln(1);");
}

見慣れないものが出てきました. これをコンパイルして実行すると,1 と出力されます.

mixin は渡された引数の文字列をその場所にDのコードとして埋め込むことが出来ます. (条件がありますが,詳しい説明は省きます)

例えば,四則演算をする関数を作りたいと思ったとき.

int calc(string op)(int lhs, int rhs) {
  // Dでは~(チルダ)で文字列の連結が出来る
  return mixin("lhs" ~ op ~ "rhs");
}

void main() {
  writeln(calc!"+"(1, 2)); // 3
  writeln(calc!"-"(3, 1)); // 2
  writeln(calc!"*"(3, 4)); // 12
  writeln(calc!"/"(8, 2)); // 4
}

このようにすると関数を4つも書かなくて済みますね.

D言語に興味を持ったそこのあなた

D言語の公式ページでコードを実行して遊べます.ここ

Windows/Mac/Linux に対応してるので今すぐインストール. ここ

Windowsは上記のURLから飛んでインストーラ使って.

LinuxMacならスクリプトを叩く.

curl -fsS https://dlang.org/install.sh | bash -s

これらのD言語の魅力は使ってみて体感してください.特にC++ユーザーは気にいるはず.


明日はバターくんです.(誰かわからんぞ)

// ここに記事を貼る

#procon29 競技部門参加記

※この記事は ICT Advent Calendar 2018 3日目の記事です.

昨日はPCKモバイル部門でグランプリを獲得したびーまかです.

b-mk.hatenablog.com

結果

結果のソース

予選

P10リーグ

対戦相手 結果 勝敗
木更津 98-84
沼津 144-131
舞鶴 89-56

予選敗退 0勝3敗 リーグ4位

敗者復活戦

L7リーグ(リーグ内リーグ戦)

対戦相手 結果 勝敗
73-98
石川 69-83

リーグ内リーグ戦のため,2勝チーム同士で比較して総得点数が多いチームが通過

比較相手 結果 勝敗
高知 102-181

敗者復活 2勝0敗 リーグ1位

決勝トーナメント

対戦相手 結果 勝敗
北九州 128-124

1回戦敗退

チームメンバー

SolverのAlgorithm

この問題は,「二人零和有限確定不完全情報ゲーム」として分類できる(?) 不完全情報ゲームのAIを考えるのがめんどくさくなったので完全情報ゲームみたいな問題に置き換える.

不完全情報である理由

まず,完全情報ゲームは,「すべての意思決定ノードにおいて,今まで取られた行動などが得られる」ものである. この問題では,双方のチームの意思決定が同時であるため,上記を満たさないと考えられる.

この問題を,先手・後手に分かれ交互に手を打つ「完全情報ゲーム」とみなせば.囲碁等のAIで知られる モンテカルロ木探索(MCTS) が適用出来ると考えた. ちなみに,囲碁AIで世間を賑わしたAlphaGoはこのモンテカルロ木探索と機械学習を組み合わせているらしいが,囲碁のように各マスに同じ点数が割り振られているわけではないので,本問題に対しては機械学習は有効ではないと考えた.

モンテカルロ木探索において,次にプレイアウトするノードを決めるUCB1値という有名な計算式があるが,それだけでは弱いので,現在のマス8近傍を見て,マイナスのマスに行こうとする場合や相手マス削除の場合,重みをつけるようにした.

前日(10月26日)

実はGUIが完成していない状態だったので,飛行機やバスの移動中や旅館についてからもずっとGUI開発班の手伝いをしていた. 動くものが出来たのはAM2:00くらいだったと思う.ここで初めて指示出しの練習を始める.

一通りの確認をしたあと,エージェント役の二人には寝てもらった.当日,冷静な判断が出来なくなったら困るからだ.

司令塔である僕と,出場はしないMadofukiくんと一緒にパンフレットを眺めて対戦相手の分析をしたり,ソルバと人力で対戦してパラメータ調整を行ったりした.

AM4:30くらいに就寝.

1日目(10月27日)

AM6:00くらいに起床.全く眠気が取れていない状態で試合に臨むことになってしまった.

予行練習が始まる前に,GUIの重要な部分を実装するのを忘れていたことに気づく.自分たちが右側か左側によって見た目が変わるので反転が必要になることを忘れていた. 今後の試合で必要になってくるので実装を始めた.予行演習では反転する必要がなかったのでそこは問題ではなかった.が,GUIが落ちて何も出来なかった. 原因としては,Solverからの候補が送られて来ていない状態でGUIに候補を表示するボタンを押してしまうと死ぬことはわかった.バグを直す時間がなかったので,それを避けるように人間が頑張ることにした.

予行演習が終わったあと反転機能の実装を完了させることが出来た.

1試合目,1ターンごとにSolverが使える時間を長くしすぎたため,間に合わなくなる.人力に切り替えたものの負ける.

2試合目,1ターン目は他のターンよりも長い時間が使えるので,その分Solverも使える時間を増やした.と思っていたら,1ターン目が短くて,他のターンのほうを長くするようにしてしまい指示が間に合わなくなる.人力に切り替えたものの負ける.

3試合目,1ターンに書ける時間は問題なかった.が,盤面の入力ミスがかなり多くなってしまい,正しくSolverが探索が出来ない.GUI上では勝っていたが,当然実際の盤面と違うので負ける. どうも,冷静な判断が出来なくなってしまったのは司令塔である僕らしい.完全に寝不足だ.

旅館に戻ったあと,死ぬほど体調が悪いことに気づき,指導教員から薬をもらった. 奇しくも,この日は僕の19歳の誕生日だった.たぶん,今まで生きてきた中で一番最悪の誕生日だったように感じる.体調最悪だし,予選全敗だし.

予選の結果から,入力ミスが出ないようにGUIの改良が必須であるという意見が話し合いで出たため,3人に開発を丸投げして僕だけPM10:00くらいに寝た.

2日目,僕が冷静な判断を出来るようにするために.

2日目(10月28日)

AM6:00くらいに起床.ぼくはなんとか体調が回復していたので安心した.周りを見渡すと,敷布団も掛け布団も無しに寝ている3人が目に飛び込んできた. 正直,涙出そうになった.いつまで起きて開発していたか分からないが,彼らは相当頑張ってくれたと思う.

GUIの動作を確認するとかなり良くなっていた.1つのボタンで2人のエージェントを移動出来たり,元に戻したりするように出来ていたし,GUIの落ちるバグも修正されていた.

敗者復活戦が始まった.

1試合目.予選では座って指示を出していたが,自分は立ったほうが落ち着くことを思い出したので,椅子を外してもらって立ってやることにした. 入力ミスは少しだけあったので不安だったが,なんとか勝つことが出来た.

2試合目,またも若干のミスがあったので不安だった.結果は負けだった.待機席に戻りながら「あぁ,今年のプロコンは決勝にも上がれず終わりか」と落ち込んでいた. しかし,北九州のWA_TLEくんが,

「この盤面,ルール違反してる!!」

的なことを割と大きめな声で言っていた.驚いたぼくは,スクリーンと配られたデータを確認する.

f:id:kurokoji:20181204015300p:plain
高専プロコン競技部門2日目ストリーミングから引用

なるほど,初期位置の時点で平等じゃないやん. ということで,すぐさま運営に抗議した.この画像は抗議したときの僕の画像.

f:id:kurokoji:20181204020201j:plain
抗議しているときの僕

運営に対するヘイトが溜まっていくにつれ,他高専との絆が深まったのでそこに関しては運営に感謝したいと思う.

10分ほどしてから,運営から「再試合をします」との連絡があった.謎の拍手が起こる.何の拍手だよ.(以下のストリーミングの2:41:38ほどから)

兎にも角にも,再び決勝トーナメントに行くチャンスが与えられた.

2試合目(再試合),落ち着きを取り戻すことが出来たのか,なんと入力をノーミスでやることが出来た.試合が終わったあとに勝利を確信した.結果,勝っていた. 本当に良かった.「最後まで諦めるな」という言葉はこの現象にピッタリなのではないか.

敗者復活戦はリーグリーグという特殊なルールで,6,7チームのうち,勝率が高いチームが2つあった場合,総得点数で復活者を決めるという理不尽ルールが適用されている. なんとか突破できた.なるべく膠着した試合にならないように,人力で補正を掛けたのは良かったと思う.

決勝トーナメント,さっきルール違反に気づかせてくれたWA_TLEのいる北九州と当たった. あのIOI金メダリストと戦えると思うと怖さもあったが,ここで勝てば優勝すらいけるのではないかと内心ワクワクしていた.試合中はほとんどミスもなく,最後まで勝ち負けが分からない接戦だった.

結果,最後のターンで逆転されてしまい4点差で敗北を喫した.あとで彼らに聞くと,最後はWA_TLEくんが天才的な頭で指示を出したので勝てたらしい.流石だ.

反省

良かったこと

  • タスクをちゃんと振れた
    • 昨年は僕のワンマンチームで,ほとんどのプログラムを僕が書いてしまったのでストレスが半端なかった.この反省を生かして,ちゃんとチームメンバーにタスクを振って分散させることが出来た.
  • ちゃんとプログラムで勝てた
    • 最後まで人力でやるチームがある中で,諦めずにプログラムで戦えたのは良かったと思う.

悪かったこと

  • 練習が前日にしか出来なかった
    • 1日目のミスは明らかに練習してないことに起因する.開発を平行にやるよりも全員でGUIを先に完成させてしまったほうが良いような気がする.
  • デスマをしたこと
    • デスマをしない高専は負けると言われるが,体調を壊すと元も子もないのでやめましょう.

感想

運営に対する愚痴

ゲームの内容は良い.ただ謎の人力要素をやめてほしかった.人力要素にしてもあまりにも本質的じゃないヒューマンエラーを生んでしまうような競技はどうなんだろう.来年からは期待したい.

あと,ちゃんと問題をチェックしているのか.procon27でも再試合があったのでもう二度と繰り返してほしくない.この大会には高専生活を掛けて戦っている人もいる.そういう人たちのために真面目にやってほしい.

阿南高専生の補助学生に対する扱いがひどい.ただでさえボランティアなのにどうにかならないのか.

雑多な感想

我々沖縄高専競技部門のチームは「塗、無言、枡目にて。~私はずっとそうやって過ごしてきた~」というものだ. これは涼宮ハルヒの憂鬱長門有希のキャラクターソングである雪、無音、窓辺にて。を改変したものだが,全然言及されなくて悲しくなった.結構いい名前だと思うんだが.

今回は決勝トーナメントまで出場できた.優勝を目標にしていたが,上出来の結果だと思っている. 来年も参加したい気持ちは山々だが,僕は来年5年生で受験勉強や卒研があるので参加しない予定である.後輩にはぜひ優勝を目標に頑張ってほしい.(助け舟くらいは聞いてくれれば全然だすけども)

また,最後までついてきてくれたチームメンバーと,何かとお世話になった教授に感謝の意を表したいと思う.本当にありがとうございました.

最後に,沖縄高専競技部門の努力の結晶であるgithubリポジトリを貼って締めようと思う.みなさんお疲れ様でした.

https://github.com/kurokoji/procon29-kyogigithub.com


明日はみずきち先輩の記事

http://shimamiz-m.hatenadiary.jp/entry/2018/12/05/002027shimamiz-m.hatenadiary.jp

Ubuntu18.04でneovim(or vim8)+LanguageClient-neovim+clangdでC++の補完をする

普段はMacで使ってますが,TwitterUbuntuで上手くいかなかった人がいるみたいなのでやってみました.

必須なやつ

インストール手順

dein.vimとneovimのインストール手順は省きます.(他に書いてる人がたくさんいるので)

clangd

clangも一緒にインストールしておきます

sudo apt install clang-6.0 clang-tools-6.0
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-6.0 100
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-6.0 100
sudo update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-6.0 100

LanguageClient-neovim

tomに書いてください.tomlに書いてない場合,適宜読み替えてください.

[[plugins]]
repo = 'Shougo/dein.vim'

# vim8用
[[plugins]]
repo = 'roxma/nvim-yarp'
if = "!has('nvim')"

# vim8用
[[plugins]]
repo = 'roxma/vim-hug-neovim-rpc'
if = "!has('nvim')"

[[plugins]]
repo = 'Shougo/deoplete.nvim'
hook_add = '''
let g:deoplete#enable_at_startup = 1
'''

[[plugins]]
repo = 'autozimu/LanguageClient-neovim'
rev = 'next'
depends = ['deoplete.nvim']
build = 'bash install.sh'
hook_add = '''
set hidden
let g:LanguageClient_serverCommands = {
      \ 'cpp': ['clangd'],
      \ }
let g:LanguageClient_loadSettings = 1
let g:LanguageClient_hasSnippetSupport = 0

set completefunc=LanguageClient#complete

nnoremap K :call LanguageClient#textDocument_hover()<CR>
nnoremap F :call LanguageClient#textDocument_formatting()<CR>
'''

結果

同様に,prabirshrestha/asyncomplete-lsp.vimを使ってもできます. これに関してはkutimoti氏が記事を書いてくれているのでどうぞ.

kutimoti.hatenablog.com

また,cquery-project/cqueryなどでも同様です.

kutimoti.hatenablog.com