AtCoder反省会してたはずが気が付くと規格読みながらcpprefjpにプルリク投げてた

はじめに

本記事では,私の所属サークルでAtCoderコンテストABC218の反省会していたはずだったのに,気が付くとC++の規格書を読みながらcpprefjpにプルリクエストを送信していた,という話をします. 本記事は,室蘭工業大学 Advent Calendar 2021の3日目に割り当てられた記事です.

adventar.org

本記事の内容は,【LT会】Acompany競技プログラミングLT会 #1で発表したLTの内容をもとにしています.

acompany.connpass.com

経緯

まず,C++の規格書を読みながらcpprefjpの記事を修正するまでの経緯を述べます.

私の所属サークルのAtCoder反省会

私の所属サークルにはAtCoder反省会という会があります. これは,AtCoderのコンテストの時間になったら,オンラインで集合してコンテストに参加し,コンテスト終了後にそのコンテストの問題の解き直し,感想戦,解説などを行う会です. この会は,競技プログラミングをする部員同士で交流することを目的として始まりました.

ABC218

2021年9月11日に開催されたABC218では,100分間のコンテストの内82分経過した時点でA,B,D,Cを解くことができ,残り18分間でEに挑みました.

atcoder.jp

辺を報酬に関して昇順に並べた上で,グラフが連結になるまで,報酬が低い方からまだ連結していないノード同士を繋ぐ辺をグラフに追加していけば良さそうだなあと感じ,DSUを使って実装しました. しかしながら,結果はWrong Answerで,コンテスト中にデバッグを完了することができませんでした.

atcoder.jp

ABC218のAtCoder反省会

ABC218が終了した後,所属サークルのAtCoder反省会が始まりました. Eの解説をチラッとだけ見てみると,コンテスト中に思いついた解法は想定解法と同じっぽい雰囲気がありました. 運の悪いことにサンプルのテストケースは通ってしまっていたので,テストケースをいくつか自作してプログラムの内部状態の変化を確認しました. そうすると,辺を報酬に関して昇順に並べる処理に失敗していることに気が付きました. この処理は以下のようにしてstd::multiset<tuple<ll,ll,ll>>型の変数Cに辺の情報を追加することで実装していました.

/* 省略 */
using ll = /** __int128; //*/ std::int_fast64_t;
/* 省略 */
void solve(){
  /* 省略 */
  auto cmp = [&](tuple<ll, ll, ll> const &t0, tuple<ll, ll, ll> const &t1) {
    return get<0>(t0) < get<0>(t1) || get<1>(t0) < get<1>(t1) ||
           get<2>(t0) < get<2>(t1);
  };
  std::multiset<tuple<ll, ll, ll>, decltype(cmp)> C(cmp);
  /* 省略 */
}

ここで,Cにはそれぞれの辺の情報が次のようなタプルとして格納されます.

  • 要素として報酬,一つ目のノード,二つ目のノードをこの順に持つタプル

また,Cに格納される辺が報酬に関して昇順に並ぶようにするために,コンパレータcmpを指定しています.

このコンパレータは,とりあえずtuple<ll, ll, ll>のコンパレータのデフォルトの実装と同じものを用意しておいて,後で考察しながらいじれるようにするつもりでした(実際にはデフォルトの実装からいじる必要はありませんでした.). しかし,デフォルトの実装のつもりで記述したコンパレータが実際にはデフォルトの実装と異なったものとなっていました. これが,Wrong Answerを解消できなかった根本的な原因であり,ここに誤りがある限りいくら考察を進めてもAcceptedには辿り着けません.

コンパレータの実装の誤りに気が付いたので,以下のサイトの記事を読んで正しい実装を確認することとしました.

cpprefjp.github.io

この記事を読んでいると,記事のコードの中に「`」が紛れ込んでいるのを見つけたため,この1文字を削除するだけのFix typoプルリクエストを軽い気持ちで送りました.

github.com

その時点では,「翌日にはマージされているだろう,また一つ,Fix typoのプルリクエストを送ってしまった」と思っていました.

プルリクエストがマージされるまで

翌日,プルリクエストにコメントが送られました. 私は,コメントの内容を以下の通りに解釈しました.

  • 私がプルリクエストを送る前の説明が,そもそも不完全であったため,それを完全なものとする必要がある.
  • 私がプルリクエストを送る前の説明が,C++17における説明であり,C++20においての説明にする必要がある.

私は,元のプルリクエストの修正範囲を超えていると感じ,プルリクエストを閉じようとしました. そうしたところ,C++17における説明を完全なものとすることにも意義があるため,その修正をこのプルリクエストでやったらどうかという主旨のコメントが追加されました. C++の規格書として参照すべきURLも与えられていたため,これを見ながら修正を行いました. 修正は,基本的に規格書の内容を日本語訳することにより試みました. これに合わせて,プルリクエストのタイトルを変更し,レビューをお願いしました.

これに対して返されたレビューについて,私は以下の通りに解釈しました.

  • 修正内容には問題は無い.
  • コミットの単位をsquashにより修正するべきである.

squashをするのが初めてだった私が少し自信なさげに「やってみます」と返答したところ,なんとレビューコメントにsquashのやり方まで追記してくれたのでした. コミットのsquashを行うと,プルリクエストは無事マージされました.

私のFix typoでないプルリクエストで,個人的なプロジェクト以外のリポジトリにマージされたのはこれが初めてでした.

Fix typo以外でマージされた初めてのプルリクエス

ABC218のE問題のAccepted

コンパレータはstd::tupleのデフォルトの動作で十分であると考察したため,デフォルトのコンパレータを使用するように変更したところ,デバッグも順調に進み無事Acceptedとなりました.

atcoder.jp

誤りのあったコンパレータを,あえて明示的にデフォルトの動作と同様の動作をするように修正すると以下のようになります.

/* 省略 */
using ll = /** __int128; //*/ std::int_fast64_t;
/* 省略 */
void solve(){
  /* 省略 */
  auto cmp = [&](tuple<ll, ll, ll> const &t0, tuple<ll, ll, ll> const &t1) {
    return get<0>(t0) < get<0>(t1) ||
           ((!(get<0>(t1) < get<0>(t0))) &&
            (get<1>(t0) < get<1>(t1) ||
             ((!(get<1>(t1) < get<1>(t0))) && get<2>(t0) < get<2>(t1))));
  };
  /* 省略 */
  std::multiset<tuple<ll, ll, ll>, decltype(cmp)> C(cmp);
  /* 省略 */
}

【LT会】Acompany競技プログラミングLT会 #1

私の友人の所属先が開催するLT会で,競技プログラミングに関するテーマでLTを募集していました. 私も何か発表してみたいなあと考え,上記の内容を5分程度にまとめてLTを申し込みました. LTに対しては,予想していたよりも良い反応をいただくことができ,発表した甲斐があったと思いました. また,LT後の雑談も盛り上がり,Androidアプリの開発やRustをおすすめされたりしました.

私のLTの他に,2つのLTがありました. 一つは,作問をすることにより,解けなくても思考を続ける粘り強さを養いましょう!という内容でした. 解ける解けないといったことの他に,考察そのものの楽しさを忘れないようにしたいと思いました. もう一つは,ICPCに出場しましょう!という内容でした. ICPCに出場したり,コーチとしてついて行ったりしたことを思い出して,懐かしく感じました.

LT会の後で,LTの内容が競技プログラミングと少しずれていたかもしれないと思いました. 友人にそのことを言ってみると,ずらし方が巧妙で気が付かなかったと言われました.

まとめ

やったこと

  • 所属サークルのAtCoder反省会でABC218Eをデバッグした.
  • cpprefjpのtypoを発見してプルリクエストを投げた.
  • 元の説明が不完全だったため,規格を読んで書き直した.

得たこと

  • ABC218EでAcceptedとなるコードを提出できた.
  • C++の規格を読む」という体験をした.
  • コミットの内容だけでなく,形式まで意識してプルリクエストを送信した.
  • Fix typo以外のプルリクが,自分以外のリポジトリに初めてマージされた!

「KurachanActionGame」の開発に関する個人的な振り返り

私は大学で所属しているプログラミングサークルで以下のようなアクションゲームを共同開発しました.

@sakaki_tohruさんには,プレイ動画を公開してくださいましたことについて,感謝いたします.

このゲームでは,プレイヤが「クラちゃん」(クラゲのような生物のイラスト)をクリックすると,クラちゃんは分裂し,プレイヤは得点を得ます. プレイヤはできるだけ高い合計得点を得ることを目指します.

このゲームに関する詳細な情報は,GitHubリポジトリを参照してください.

github.com

以下では,このゲーム開発プロジェクトに関して個人的に振り返ります.

動機

本ゲーム開発を実行しよう思い至ったのは,以下の気持ちが絡まって溶け合い,弾けた結果でした.

  • サークルの同学年の中で進学したのが自分だけで,さびしかった.
  • 前年度,サークル活動が全くできていなかったため,サークルで何かしたいという気持ちが高まっていた.
  • サークルで,既にAPG4b勉強会とSiv3D勉強会が開催されており,ゲーム開発の下地が整っていると感じていた.
  • サークルで,AtCoder勢力に対抗するためゲーム開発勢力を増やしたかった.

プロジェクトの開始

サークルのリモート部会に頻繁に遊びに来る3名に声をかけました. また,これを聞きつけて自分も参加したいと言ってくれた1名も加えて,全部で5名のチームでゲーム開発プロジェクトをスタートしました. サークル内で共同開発する上で工夫した点の一つは連絡手段でした. サークルのDiscordサーバ内のチャンネルの中に本ゲーム開発用の公開スレッドを作成し,本ゲーム開発での連絡は全てこれを利用しました. これにより,以下の効果が得られると考えました.

  • サークル内ではプロジェクト外のメンバにも情報が公開されるため,進行状況がサークル内で共有される.また,後輩が後から参照して参考にすることもできる.
  • スレッドは時間が経つと自動的にアーカイブされるため,プロジェクト終了後はチャンネル内で邪魔にならない.
  • サークル内ではスレッドがそもそも公開されているため,メッセージの送り先を間違えて別のチャンネルやスレッドに送ってしまっても被害が出にくい.

実際,自分も参加したいと言ってくれた1名は,本プロジェクト用のスレッドのやりとりを見たことで本プロジェクトの存在を知ってくれました.

開発期間

2021年9月12日に最初の打ち合わせを開き,2021年10月31日のリリースを行いました. リリースまでの間,毎週2〜4回集まって,毎回2時間くらい開発を行いました. 全員集まれない回もありましたが,集まれる人だけゆるく集まって開発を行いました.

開発場所

開発期間中は対面で集まって開発することができませんでした. そのため,開発はGather.town ( https://www.gather.town ) というビデオチャットプラットフォーム上で行いました. Gather.townは,Discordなどのグループ通話に比べて,「部室に集まってる感じ」があって良かったと思っています.

バージョン管理

学部の頃からずっとGitとGitHubでバージョン管理しながらプログラムを書いてきた私は,バージョン管理されていないプログラムに触わると心が不安に蝕まれるようになっていました. また,複数人で一つの成果物を作り上げる時のバージョン管理方法として,GitとGitHubを利用したものより良い方法が思いつきませんでした.

GitとGitHubを利用したバージョン管理は,慣れていないメンバにとって非常に高いハードルでしたが,GitとGitHubを使ってもらえるように使い方を教えました.

役割分担

開発では以下のように役割分担をしました.

  • 1年生2人,2年生1人:タイトルシーン,ゲームシーン,クレジットシーン,ルールシーンの開発
  • 4年生1人:上記以外のプログラムの作成,プログラミングのサポート
  • 私:イラストの作成,プログラミングのサポート,日程調整

ここでプログラミングのサポートとは,画面共有してデバッグを手伝ったり,設計やアルゴリズムに関する相談を受けたり,レビューと動作確認したり,C++,Siv3D,Git,GitHubVisual Studioなどについて教えたりといったものです. また,日程調整とは,各回の終了時に「次はいつ集まれますか?」と聞いて次回集まる日時を決めるものです. どんなにプログラミングの得意なメンバを集めたとしても誰かが日程調整をしないとプロジェクトは空中分解してしまいます.実際,過去に一度そうして空中分解したプロジェクトに参加したことがあります.

上記ような役割分担にしたのは,以下が理由となっています.

  • 私以外のメンバが,できるだけプログラミングの楽しさに触れられるようにしたかった.
  • 1,2年生をできるだけ手厚くサポートしたかった.
  • オリジナルのイラストが描いて,それがゲームの中で動くさまを見てみたかった.

また,各シーンの開発では基本的にシーン単位で役割を分担しました. 各メンバには,それぞれの担当するシーンについて,動きやデザインの実装を任せました. これにより,それぞれの作業の独立させやすくなり,作業の競合を避けることができると考えました. 欠点として,全体の統一感が損なわれがちになることが挙げられますが,今回の開発ではそれは気にしないこととしていました.

成果物

まず,ゲーム開発の最低限の要求として,遊ぶことが可能な実行ファイルをダウンロードできる状態にする,ということは達成されました. プログラミング経験の浅い1年生もいるチームでゲームを完成させたという成果は,個人的に満足して良いものだと感じています.

一方で,成果物は以下に挙げるような問題点も抱えています.

  • 「パラちゃん」という既存のゲームに類似しており,新規性があるとは言い難い.
  • ハイスコアデータがテキストファイルで保存されるため,プレイヤによる不正が容易である.
  • 全体のデザインなどに統一感が無い.
  • macOS版ではBGMが正常に再生されない.

しかしながら,面白いゲームを作ることや完成度の高いゲームを作ることは私がこのプロジェクトを開始した動機には含まれていないため,これらの問題点については個人的にはあまり気になっていません.

一応,オープンソースとしてソースコードも公開してますので,もし改善案などがあれば,Issue,Pull request,forkなどは歓迎します.

作成したイラスト

個人的な成果として,作成したイラスト「クラちゃん」を以下に示します.

良かったこと

まず,前述の成果物を完成させて大きな達成感を得られたことはとても良かったです. 以下では,このゲーム開発を通じてと感じたものづくりの楽しさと共同開発の楽しさについて述べます.

ものづくりの楽しさ

開発中,ゲームが少しずつ出来ていく様子や自分が描いたイラストがゲーム内で動くようになっていく様子を見ていると楽しかったです. 私の役割の一つであるイラストでは,「クラちゃん」というクラゲのような生物を自分の手によってこの世に生まれさせることが創造的で楽しかったです.

共同開発の楽しさ

今回の開発では他のメンバのプログラミングのサポートもしましたが,イラストの作成に注力することができたと思っています.実際,感覚的には自分の作業時間の50%くらいはイラストの作成をしていた気がします. これは,すなわち,他のメンバにコーディングを任せていたということであり,他のメンバと協力してゲームを完成させたという実感の源泉にもなっています.

また,今回のプロジェクトにはこれまで自分が参加した共同開発ではあまり感じたことのなかった感動もありました. 今まで共同開発は,同学年でチームを組んでいたのですが,このプロジェクトではチームは下級生とチームを組みました. そして,プロジェクトはチームのメンバにプログラミングを教えながら進めました. このような進め方は初めてでしたが,チームのメンバがプログラミングに慣れていく様子やアドバイスしたことが実装に反映されていく様子を見て感動していました.

上級生にプログラミングのサポートに割り当てるという今回の役割分担は,プログラミングの得意なメンバだけが作業を独占してしまうということが発生せず,メンバ全員が開発に関わることができたため,正解だったと感じています.

悪かったこと

プロジェクトの名前の決め方の雑さ

開始時にプロジェクトの名前を決めていませんでした. これはゲームタイトルが決定してから,それをプロジェクト名にしようと思っていたためです. 「パラちゃん」のようなアクションゲームを作ろうということだけは決まっていたため,GitHubに組織とリポジトリを作成するときに雑に「ParachanActionGame」と命名してしまいました. これは良くありませんでした. なぜなら,リポジトリの名前は後から変更できるのですが,組織の名前は後から変更できないからです.

プロジェクト名はゲームタイトルとは独立に最初に決めるべきでした.

プログラミングのサポートの見通しの甘さ

プログラミングの勉強を始めてわずか半年の1年生が2人いる中で,GitとGitHubでバージョン管理しながら,クラスベースのプログラミングを進めていくというのは,振り返ってみると非常にチャレンジングだったと感じています. それでも,プロジェクト開始前は,私がプログラミングのサポートをしながらプロジェクトを無理なく進めることはできるだろうと思っていましたが,その見通しは甘いものでした. プロジェクトを進める中でプログラミングのサポートを実際に行ってみると,一つのことを教えるためにはその背景にある多くのことを教える必要があり,とてもハードであるということに気が付きました.

今回はラッキーなことに,自分から参加したいと言ってくれた4年生がメンバに加わってくれたため,2人で協力しながらプログラミングのサポートをすることができ,何とかゲームを完成させるまで至ることができたと思っています.

まとめ

  • サークルで幅広い学年のメンバで構成されたチームを結成し,ゲームを共同開発した.
  • KurachanActionGameという名前のアクションゲームを完成させ,実行ファイルをGitHubで一般に公開した.
  • プログラミングやバージョン管理に慣れてないメンバのサポートをしながらプロジェクトを進めた.
  • 開発中はものづくりや共同開発の楽しさを感じ,完成時には達成感を味わった.

謝辞

プロジェクトに付き合ってくれたメンバの皆さんには,一緒に開発してくれたことについて大変感謝しております. 本当にありがとうございました! これからもよろしくお願いいたします!

麻雀プログラミング

定義

麻雀プログラミングとは,麻雀をしながらプログラミングをするというもので,具体的には以下の通りに実施します.

  1. 5人以上の参加者を集めます.
  2. 参加者の中から麻雀プレーヤーを無作為に4人を選択します.
  3. 麻雀プレーヤーとして選択された参加者は麻雀の一局戦(または東風戦)をします.
  4. 2で選択されなかった参加者はプログラミングをします.
  5. 麻雀が終了するたびに2から4までを繰り返します.

着想

麻雀プログラミングはカラオケ麻雀という遊びから着想を得ました. カラオケ麻雀とは,カラオケで歌いながら同時に麻雀もするという非常にチャレンジングかつエキサイティングな遊びです. この遊びが生まれたのは,友人と遊ぶ計画を立てていた時でした.カラオケと麻雀のどちらで遊ぶかで迷っていた我々は,大学で培った洞察力と常識に囚われない発想力に基づいて,カラオケと麻雀の二つが背反ではないという天才的な発見をするに至り,さらにカラオケと麻雀を同時に遂行するカラオケ麻雀という革新的なアイデアを導き出したのです. カラオケ麻雀を実行してみると,カラオケも麻雀も楽しめるということの他にも予想外の知見を得ることができました. それは,カラオケと麻雀のどちらか一方だけに集中力を注ぐことができないために,常に不完全燃焼となり,長時間続けても飽きや疲れを感じないということでした.

そして,「複数の楽しいことを組み合わせることによりもっと楽しい時間を過ごせる」という理屈と「あえて過度な集中を抑制することにより飽きや疲れを感じずに長時間続けることができる」という理屈をプログラミングに応用できないかと考えた結果,上記の麻雀プログラミングに思い至りました.

期待できること

麻雀プログラミングによって期待できることを以下に示します.

  1. 楽しい時間:麻雀やプログラミングはそれぞれを個別に体験しても楽しい時間を過ごすことができます.いわんや,麻雀とプログラミングの両方を体験できる麻雀プログラミングをや.なおさら楽しい時間を過ごせるのではないかと期待しています.
  2. 生産量の向上:麻雀プログラミングでは,麻雀とプログラミングを行ったり来たりすることにより,プログラミング(および麻雀)に対する過度な集中を抑制します.また,一局戦(または東風戦)が終了するタイミングで交代となるため,キリの悪いところでプログラミングを中断することができます.これにより,疲れや飽きを感じてしまうことや,達成感を得て集中力を切らしてしまうことを防ぐことができます.以上から,プログラミング(および麻雀)の質は下がるかもしれませんが,それを補うくらい長い時間をプログラミング(および麻雀)に注ぐことができ,むしろ生産量(得られる楽しさの量)は向上するのではないかと期待しています.

検証

実はまだ,麻雀プログラミングを実施したことがありません. 麻雀プログラミングの効果の検証結果については,実際に麻雀プログラミングを実施した後で書きます.

また,もし麻雀プログラミングを実施してみた方がいらっしゃったら,コメントをいただけると嬉しいです.

GitHub人狼 -- みんなでプログラムを書いてカジュアルに遊びたい!

目的

本記事の目的は,みんなでプログラムを書いてカジュアルに遊ぶためのパーティゲームGitHub人狼」を提案することです. 以下では,このゲームの遊び方,提案の背景,期待する効果,懸念点について説明します.

遊び方

ゲームの概要

GitHub人狼」は,ソフトウェア開発プラットフォーム「GitHub」を利用するゲームです.開発チームに紛れ込んだ「人狼」の妨害を回避しながら「村人」達が成果物を完成させることを目指します.

  • 参加人数:3人以上
  • 所要時間:2時間以上

勝利条件

「村人」と「人狼」の勝利条件を以下に示します.

  • 村人の勝利条件は,開発するべき成果物の「仕様」が定義されており,かつ,以下が全て満たされることです.

    1. 最終的な成果物が実行可能である.
    2. 定義した全ての仕様が実装されている.
  • 人狼の勝利条件は,開発するべき成果物の「仕様」が定義されており,かつ,以下のどちらかが満たされることです.

    1. 最終的な成果物が実行可能でない.
    2. 定義した仕様のうち実装されていないものがある.

勝利条件が満たされない場合またはルール違反をした場合には敗北となります.

ゲームの流れ

ゲームは以下の流れで進行します.

  1. 仕様定義
  2. リポジトリ作成
  3. ロール決定
  4. 開発
  5. 成果発表
仕様定義

開発チーム内で相談し,開発するべき成果物の「仕様」を定義します. 仕様は,成果物に実装するべき動作についてスライドなどにまとめます. 仕様定義に割くことのできる時間は,あらかじめ決めておきます.

リポジトリ作成

GitHub上で開発チームのリポジトリを作成し,メンバーはそれぞれフォークして開発環境を整えます.

ロール決定

開発チームを構成するメンバーそれぞれのロールを決定します. ロールは「村人」と「人狼」のどちらか1つをランダムに割り当てます. ただし,村人の人数が過半数以上となるようにします.

開発

村人は定義した仕様を満たすプログラムの実装を進め,人狼はこれを妨害します. 具体的には,以下の手順を繰り返します.

  1. 開発チームのメンバーはそれぞれプログラムを書く.
  2. プログラムを書いたメンバーはプルリクエストを送る.
  3. プルリクエストに対し,メンバーはマージするかどうかについて匿名で投票する.
  4. 投票でマージの得票数が過半数以上になった時,プルリクエストをマージする.

開発に割くことのできる時間は,あらかじめ決めておきます.

成果物発表

開発チームのメンバーは互いにロールを公開し,成果物発表を行って勝利条件を確認して勝敗を決定します. 成果物発表では,村人は成果物発表を行い,成果物が実行可能であることおよび全ての仕様が満たされていることを示します. 人狼は,成果物が実行可能ではないことまたは満たされていない仕様があることを指摘します.

オプショナルルール

  • 人数に余裕がある場合はゲームマスターがいても良いです.ゲームマスターは,時間管理,ロール決定,票の収集,過半数の得票があったプルリクエストのマージ,勝利条件の確認を行います.
  • 全ての人狼が合意した場合,投了した上で開発に協力することができます.この場合,村人は勝利となり,人狼は敗北となります.
  • GitHubを利用するのではなく,GitBucket,GiteaまたはGogsなどを利用しても良いです.

提案の背景

私は,自分が所属するプログラミングサークルにおいて,以下のような部員同士の交流に関する問題を解決し,プログラミングに触れながらカジュアルに交流したいと考えていました.

  • プログラミング:それぞれの興味がバラバラで個人プレーになりがちである.
  • 勉強会:コミュニケーションが一方的になりがちで,なかなか盛り上がらない.
  • チーム開発,ハッカソン競技プログラミング:ハードルが高く,カジュアルじゃない.
  • 麻雀,人狼,AmongUsなどのパーティゲーム:交流はできるが,そもそもプログラムを書かない.

そして,共同開発に人狼やAmongUsのような駆け引き要素を取り入れることはできないかと考えた結果,上の「GitHub人狼」を思い付きました.

期待する効果

GitHub人狼」で遊ぶことにより,以下の効果が期待できると思います.

  • プログラミングを通してカジュアルに交流できる.
  • プログラミングをパーティゲームとして楽しめる.
  • スキルに差があってもゲームとして成り立つ.
  • 共同開発っぽい体験ができる.
  • プログラミングとGitの練習ができる.

懸念点

以下の懸念点が挙げられます.

  • プログラミングとGitの基本スキルが必要となる.
  • 仕様が曖昧だと勝敗も曖昧になってしまう.
  • 現状ではテストプレイが不十分である.

遊んでみた感想

遊んでみてから書きます.

新入生向けプログラミングハンズオン

目的

本記事は SAMIT Online! 21.04 で行われた発表「新入生向けプログラミングハンズオン 準備」,「新入生向けプログラミングハンズオン」の資料です.

本記事のターゲットは,プログラムを書いたことはないがプログラミングに興味を持っている新入生です. 本記事の目的はそのような新入生がプログラミングを勉強するモチベーションを維持できるようになることです. 本記事は,「自分の手でプログラムを書き,それが動作することを実感する」という体験が勉強のためのモチベーションの維持に繋がるという考えに基づき,新入生にそのような体験してもらうためのハンズオンの資料を提供します.

本記事のハンズオンでは,

  • 新入生がプログラミングの楽しさを知る前に躓くことがないようにすること,
  • プログラミングを勉強を始めるきっかけになる楽しい体験となること

を意識し,以下の4つの条件を満たすようなハンズオンを用意しました.

  • 環境構築の手間をできるだけかけないこと
  • 作成するプログラムの規模が大き過ぎないこと
  • 作成するプログラムが対話的に動作すること
  • 作成するプログラムがグラフィカルに動作すること

このハンズオンでは,ブラウザ上でプログラムを書いて実行できるサービスであるCodePen( https://codepen.io )を利用し,最終的に,ボタンを押すたびにサイコロが振られ,出た目の画像が表示されるプログラムを作成します.

本記事の後半では,本ハンズオンに挑戦してみた後でプログラミングの勉強を始めてみようと思った新入生のために,おすすめのプログラミング勉強法も示しています.

本記事の最後では,YouTubeで開催されたSAMIT Online! 21.04の様子を示しています.

ハンズオンの準備

ブラウザで https://codepen.io/pen/ を開くとCodePen上で自分のプログラムを作成するページが表示されます.

FireFox上でCodePenのページが表示される.
ブラウザでhttps://codepen.io/pen/にアクセスした様子(HTML編集エリア(上段左部),CSS編集エリア(上段中部),JS編集エリア(上段右部),実行結果エリア(下段))

これで本ハンズオンの準備は完了ですが,作成したプログラムを保存する場合にはCodePenのアカウント登録が必要となります. 必要に応じてアカウント登録を行ってください.

ハンズオン

以下に本ハンズオンで取り組んでもらいたい演習を示します. 各演習はそれぞれ以下のように構成されています.

  1. プログラムと実行結果
  2. 解説
  3. 改造の例

各演習のプログラムは,左側のHTMLまたはHTMLとJSの部分に示されています. 各演習の実行結果は,右側のResultの部分に示されています.

各演習は以下のように進めます.

  1. HTMLの部分に示された内容を前節で準備したCodePenのHTML編集エリアに入力し,JSの部分に示された内容を前節で準備したCodePenのJS編集エリアに入力する.
  2. 実行結果の部分に示された内容と同様の内容が前節で準備したCodePenのResultエリアに表示されることを確認する.
  3. 解説を読んでみる(わからない部分は,読み飛ばしても良いです.).
  4. 改造の例を参考にしつつ,自分なりにプログラムを改造してみる.

演習:HTMLでテキストを表示する

プログラムと実行結果

See the Pen bGgmNzP by Jumpaku (@jumpaku) on CodePen.

解説

HTMLとはWebページなどの画面構成を定義する言語です. HTMLは「要素」(<p>こんにちは</p>など)を組み合わせて記述します. 要素とは「タグ」(<p></p>など)によって挟まれた文字列です. <p></p>で挟まれたp要素は一つの段落を表します. HTMLに記述された内容がブラウザに読み込まれると,ブラウザは読み込んだHTML解釈し,解釈した内容をディスプレイに表示します.

上のプログラムのHTMLではタグ<p>とタグ</p>によって「こんにちは」というテキストを挟んだ<p>こんにちは</p>という要素が記述されています. ブラウザはこのHTMLを解釈し,「こんにちは」というテキストを表示します.

改造の例

「こんにちは」とは別のテキストを表示するように改造してみる.

演習:HTMLでボタンを表示する

プログラムと実行結果

See the Pen display-button by Jumpaku (@jumpaku) on CodePen.

解説

上のプログラムのHTMLでは<p>ボタンの表示</p>というp要素と<button>押しても何も起きない</button>というbutton要素が記述されています. button要素は一つのボタンを表します. ブラウザはこのHTMLを解釈し,「ボタンの表示」というテキストと「押しても何も起きない」と書かれたボタンを表示します.

改造の例
  • ボタンを増やしてみる.
  • HTMLに<strike>取り消し線</strike>を追加してみる.

演習:JavaScriptからHTML上のテキストを設定する

プログラムと実行結果

See the Pen XWpxJLR by Jumpaku (@jumpaku) on CodePen.

解説

JavaScriptとは様々な処理を実行することができる言語です. JavaScriptに記述された内容がブラウザに読み込まれると,ブラウザは読み込んだJavaScriptを解釈し,処理を実行します. JavaScriptを用いることでHTMLに記述された内容を表示することができます.

まず,上のプログラムのHTMLには要素<p id="text"></p>が記述されています. この要素は表示するべき文字列を持っていません. また,この要素はタグにはid="text"が記述されています. このようにタグに付け加えられたものを「属性」と言います. 「属性」は要素に様々な情報を付け加えます. 例えば,id=で始まるid属性は,ある要素に別の要素と区別するための名前を付け加えます.

JavaScriptは上のプログラムのJSの部分に示されています.

let e = document.querySelector("#text");

の部分はeという名前の変数を用意し,そこにHTML上から探し出してきた要素を格納しています. 変数とは値を格納することのできる名前を持った箱のようなものです. 変数を用意するときは,let 変数名 = 変数に格納する値;と記述します. このとき,変数名には大文字または小文字のアルファベットと数字を使って好きな名前をつけることができます. documentはHTMLそのものを表すものです. querySelectordocumentが持っている関数で,指定された要素を探す処理を実行します. 関数とはまとまった処理に名前を付けて繰り返し実行できるようにしたものです. 関数を実行するためには関数の名前の後ろに()を付け,その間に処理に必要なデータを記述します. document.querySelector("#要素のid属性")と記述することで,指定したid属性を持つ要素を探し出すことができます.

e.textContent = "こんにちは";

の部分は変数eが持つtextContentという変数に「こんにちは」という文字列を格納しています. textContentは要素が持つ文字列を表す変数です. 文字列はプログラムの記述と区別するために"で囲む必要があります. これにより,HTMLのp要素は文字列を持たないにも関わらず,実行結果で「こんにちは」と表示させることができるようになります.

改造の例

HTMLはそのままでJavaScriptだけを編集することにより,「こんばんは」と表示するように変更してみる.

演習:ボタンを押された時の処理を実装する

プログラムと実行結果

See the Pen add-process-onclick by Jumpaku (@jumpaku) on CodePen.

解説

このプログラムは,ボタンが押されるたびに,ボタンが押された回数をカウントし,回数を表示します.

HTMLのbutton要素にはonclick=で始まるonclick属性が付け加えられています. onclick属性は押された時に実行するJavaScriptのプログラムを指定することができます. このプログラムではボタンのonclick属性にupdate()と記述されているため,ボタンを押すとupdateという関数が実行されることになります.

update関数はJavaScriptの方で次のように定義されています.

function update() {
  count = count + 1;
  counter.textContent = count;
}

関数の定義は

function 関数の名前(処理に必要なデータ1, 処理に必要なデータ2, ...){
  実行する処理1;
  実行する処理2;
  ・
  ・
  ・
}

と記述します. このとき,関数の名前は変数名と同様につけることができます. このプログラムのupdate関数は以下の処理を実行するように定義されています.

  1. 変数countcount1を加えた数を格納する.
  2. 変数counterが持つ変数textContentに変数countの値を格納する.

以上のようにJavaScriptで定義した関数の実行をbutton要素のonclick属性に記述することで,ボタンを押すたびに指定した処理を実行することができます.

改造の例

2個のボタンを用意し,それぞれのボタンが押された回数の合計を表示するように改造してみる.

演習:乱数を使用する

プログラムと実行結果

See the Pen random-number by Jumpaku (@jumpaku) on CodePen.

解説

このプログラムは,ボタンが押されるたびに,0以上1以下のランダムな実数を表示します. JavaScriptではMath.randomという関数を使用することで,0以上1以下のランダムな実数を生成することができます.

改造の例
  • 生成した乱数に1を足すことで2以上3以下のランダムな実数を表示するように改造してみる.
  • 生成した乱数に100をかけることで0以上100以下のランダムな実数を表示するように改造してみる.ただし,JavaScriptではかけ算は×ではなく*という記号が使用されることに注意してください(例えば,Math.random()×100ではなくMath.random()×100と記述することになります.).

演習:乱数の値に応じて表示するテキストを変更する

プログラムと実行結果

See the Pen display-dice-number by Jumpaku (@jumpaku) on CodePen.

解説

このプログラムは,ボタンが押されるたびに,1以上6以下のランダムな整数を表示します.

このプログラムのdice関数はMath.random()により生成した0以上1以下のランダムな実数を1以上6以下のランダムな整数に変換します.

具体的には以下の条件に応じた処理を実行します.

  • もし変数valueが1.0/6.0未満なら,変数diceValueに1を格納する.
  • 上の条件を満たさず,もし変数valueが1.0/6.0未満なら,変数diceValueに1を格納する.
  • 上の条件を満たさず,もし変数valueが2.0/6.0未満なら,変数diceValueに2を格納する.
  • 上の条件を満たさず,もし変数valueが3.0/6.0未満なら,変数diceValueに3を格納する.
  • 上の条件を満たさず,もし変数valueが4.0/6.0未満なら,変数diceValueに4を格納する.
  • 上の条件を満たさず,もし変数valueが5.0/6.0未満なら,変数diceValueに5を格納する.
  • 上の条件を満たさないなら,変数diceValueに6を格納する.

ただし,JavaScriptでは割り算は÷ではなく/という記号が使用されることに注意してください(例えば,1.0÷6.0ではなく1.0/6.0と記述することになります.).

dice関数はこの処理を,「条件に応じた処理」を記述するため構文を利用して記述されています. 「条件に応じた処理」を記述するため構文を以下に示します.

if (条件式1) {
  条件式1が真の時の処理;
} else if (条件式2) {
  条件式1が偽で条件式2が真の時の処理;
}
・
・
・
 else {
  上の全ての条件式が偽の時の処理;
}
改造の例
  • 1から5まではそれぞれ1/10の確率で表示され,6は5/10の確率で表示されるように改造してみる.
  • 1から10までが等しい確率で表示されるように改造してみる.

演習:HTMLのキャンバスに図形を描画する

プログラムと実行結果

See the Pen render-on-canvas by Jumpaku (@jumpaku) on CodePen.

解説

このプログラムはHTMLのcanvas要素に対して,JavaScriptを使用して長方形や円などの図形の描画処理を実行することで文字だけでなく,図形を表示します.

let ctx = e.getContext("2d");

の部分では,変数eが持つgetContext関数を実行することで2次元の「レンダリングコンテキスト」というもの取得し,これを変数ctxに格納しています. レンダリングコンテキストは,以下に示すような,図形を描画するための様々な関数や変数を持っています.

  • strokeStyleは直後に描画する図形の輪郭の色を格納する変数
  • fillStyle:直後に描画する図形の内部の色を格納する変数
  • strokeRect(左上の点のx座標, 左上の点のy座標, 高さ, 幅):長方形の輪郭を描画する関数
  • fillRect(左上の点のx座標, 左上の点のy座標, 高さ, 幅):長方形の内部を描画する関数
  • beginPath():図形の作成を開始する関数
  • arc(中心のx座標, 中心のy座標, 半径, 0, Math.PI * 2):円を作成する関数
  • stroke()beginPath()で作成を開始した図形の輪郭を描画する関数
  • fill()beginPath()で作成を開始した図形の内部を描画する関数
  • clearRect(左上の点のx座標, 左上の点のy座標, 高さ, 幅):長方形領域に描画されている図形を消去する関数
改造の例
  • 縦長の長方形を表示するように改造してみる.
  • 以下の条件を満たすような円と長方形が表示されるように改造してみる.
    • 条件:長方形の頂点が円周上に配置される.

演習:サイコロを振るアプリケーションを作成する

プログラムと実行結果
解説

このプログラムでは,ボタンが押されるたびにサイコロが振られ,出た目が表示されます.

詳しい解説は省略します. これまでの内容を踏まえて,解読してみてください.

改造の例

サイコロの数を増やしてみる.

おすすめのプログラミング勉強法

最後に,私がおすすめするプログラミング勉強法を以下に示します.

SAMIT Online! 21.04とJumpakuの発表の様子

  • SAMIT Online! 21.04の全体