勤務日自動調整アプリ「シルバーカレンダー」をリリースしました

始めに

自動で勤務予定を入れられる自作サービス「シルバーカレンダー」をリリースしました。

この記事では、「シルバーカレンダー」の紹介と制作の過程について書いていきます。

アプリ概要

シルバーカレンダーというサービスは、 嘱託職員などの年間日数で労働契約している方が 自分で勤務予定を調整する手間を軽減するための 勤務日自動調整サービスです。 条件を入れて自動で勤務予定の配置ができます。

https://silver-calendar.com

使い方

勤務予定のための条件を入れます。今の所、期間と勤務日数、曜日ごとの予定を入れるようになっています。

条件を作成し、横にある適用ボタンを押すと自動で予定が入ります。

「連携」からGoogleカレンダーに反映できます。

どうしてこのアプリを作ろうと思ったのか?

自作サービスを作るにあたり自分でアイデアをいくつか考えたのですが、調べてみると既にそういったアプリは存在しておりネタ切れに悩んでいました。

そこで周りの人にもアプリのアイデアがないか聞いて周ったところ、父から勤務予定の調整アプリがあると便利だという話を聞きました。

父は退職後嘱託職員として働いており、年間労働日数で契約して働いています。

もう少し詳しくいうと「年間○日出勤」ということは決まっていますが、どの日に出勤するかは自分の裁量で決めるため、自分で出勤日数を数えながら勤務予定を入れていかなければいけません。

これは結構手間がかかります。

その手間を軽減するようなアプリを作ったらどうかと考えました。

一般的な労働形態では労働者の勤務予定は会社のマネージメント担当者が決めるため、自分で勤務予定を入れるというのは一般的とは言えません。

他にそういうアプリがあるか調べてみましたが、多くのシフト管理やスケジューラーアプリは一般的な労働形態を前提にしているため、条件を入力して自動で個人の勤務予定を調整するようにはなっていなそうです。

業務で使えるようなアプリを作りたかったのもあり、それで勤務調整のアプリを作ることに決めました。

技術スタック

Ruby(3.2.2)

RubyOnRails(7.0.4)

Vue.js(3.2.36)

Vite(3.2.0)

Devise(4.8.1)

PostgreSQL

Bootstrap5

GithubActions

Fly.io

技術選定の理由

まず、アプリで入力した条件や勤務予定を保存したいのでバックエンドは必要になり、バックエンドとしてはFjordBootCamp(以下FBC)で勉強してきたRailsを使うということを決めました。

次に、カレンダーを使うアプリということで月や年を切り替えたり予定を入れたりを画面遷移を挟まずスムーズに行いたいので、フロントエンドでVue.jsかReactを使うことにしました。Stimulusにも興味があったのですが、状態管理なども考慮するとVueやReactの方が適切だろうと考えました。

この2つのうちどちらを使うか本当に悩んだのですが、Vueの方を使うことに決めました。

当時FBCのプラクティス(カリキュラムのようなもの)ではVueを学習することになっており、私もVueを学習してチーム開発でも使ったのですが、自分の中でまだきちんと使いこなせている感覚がなかったため、色々なものを中途半端にやるよりも学習したものを深めていきたいと思いVueを使うことにしました。

ビルドツールにはViteを使いました。高速なビルドツールということでESbuildが出て興味を持っていたのですがVueではサポートされていないため、同じESbuild系でVueでもサポートされているViteを使うことにしました。

あとはログイン機能にDeviseを使いました。Googleカレンダーと連携するならGoogleOAuthログインだけでいいんじゃないか?という疑問があると思いますが、このアプリはGoogleカレンダー専用ではなくOutlookなど他のサービスとも連携したいと思ったので、Deviseでのパスワードログインを基本にしました。Googleカレンダーと連携するにはGoogleOAuthログインをする必要がありますが、パスワードログインしているアカウントでもアプリ内からGoogleOAuthで認証できるようにしています。 ※今の時点では連携できるのはGoogleカレンダーのみです。

苦労した点

自動調整機能の実現

入力した条件を基にカレンダーに勤務予定を自動で入れられるようにしたのですが、これが苦労しました。 複数の条件を両立させるのが特に難しく、ノートに分岐の処理の図を書きながら何度もコードを書き換えて試行錯誤をしました。 多少複雑なロジックになってしまいましたが、思ったような動きを実現することができました。

Googleカレンダーとの連携

外部APIをアプリに組み込むのがはじめての事で、技術検証の段階から非常に苦労しました。

OAuth認証のことも最初はさっぱり仕組みがわからず、技術検証はGoogleCalendarAPIに関することだけで2ヶ月以上かかりました。

FBCの先輩にGoogleカレンダーAPIを使っていた方がいたため、教えてもらったりコードを参考にさせてもらったりすることで少しずつわかるようになりました。

技術検証の時の一番の危機は、Googleカレンダーへの反映ができないかもしれないとなった時です。

アプリで作成したカレンダーをGoogleカレンダーの方に反映する際、APIの仕様上1日単位でしかリクエストを送れないので、1年間のカレンダーだとリクエスト数が最大365件(閏年だと366件)発生します。

しかし、Googleへのリクエストは1ユーザーあたり1秒5回まで(500/100s)という制限があるらしいということがわかり、実現不可能なのでは?となった時は落ち込みました。

FBCの質問掲示板で助けを求めたところメンターの方からバッチ処理というのがあることを教えていただき、このバッチ処理を使って細かいリクエストを1回分にまとめてリクエストを行うことでなんとか実現できました。

あとはGoogleCalendarAPIを使ったアプリではGoogleにOAuthの利用申請をして審査に通らなければいけないため、アプリの使い方を動画に撮ってYoutubeにアップしたりしました。大変でしたが、新しい経験ができて面白かったです。

OptionsAPI→CompositionAPIへ書き換えた

私がFBCの課題でVue.jsを使った時にはバージョンが2系だったため、自作アプリでもその時の書き方で書いていました。しかし、3系からCompotisionAPIという書き方ができるようになったことを後で知り、今までの書き方(OptionsAPI)とは違う機能があることや可読性の面で有利なことがわかりました。

実はメインの機能をVue.jsで書いていたのもあり、ファイルがかなり大きくなっていてどこに何の機能のコードが書かれているのかわかりにくいという問題を感じていました。

そこで、OptionsAPIで書いていた部分を全てCompositionAPIに書き換えました。

終盤だったので結構な量を書き換えることになりましたが、コードが関心毎にまとめられるので可読性が大幅に改善しました。

また、CompositionAPIに書き換えたことによりprovide/injectの機能も使えるようになり、バケツリレーになっていたコンポーネント間のデータ受け渡しも少し改善しました。

OptionsとCompositionの両方の書き方を学習でき、技術選定で意図した通りVue.jsへの理解を深めることができました。

今後の展開

今後いくつか入れたい機能があります。

1つ目がOutlookとの連携機能です。現在Googleカレンダーにしか反映できませんが、勤務予定の管理にOutlookを使う人もいますので、こちらとも連携できるようにしたいと思っています。

2つ目が、祝日機能です。現在の状態では国民の祝日がカレンダーに入っていないのですが、これを入れたいと思っています。さらに、祝日を自動調整で休みにするかどうかも決められるようにしたいと思っています。

3つ目は、自動調整機能の条件の追加です。現在は「期間、勤務日数、曜日毎の予定」の3つしか条件を入れられませんが、週あたりの勤務日数を入れられるようにしたり上にも書いたように祝日の条件を追加して、さらに便利なものにしていきたいと思っています。

終わりに

今回自作アプリを一から作り上げ、本当に大変でしたが多くのことが学べました。 エレベータピッチからデザインまでご相談させていただいた町田さん、技術的なことをご相談させていただいた駒形さん、コードレビューしていただいた前田さん、日報などでアドバイスや励ましをいただいたメンターの皆様、Googleカレンダーとの連携について相談に乗ってもらったり参考にさせてもらったharuguchiさん、他輪読会やその他の機会に関わってくださったFBCの皆様、ありがとうございました!

ここで学んだことを生かし価値あるプログラムを作り出していけるよう、これからも努めていきたいと思います。

フィヨルドブートキャンプに参加して1年経ちました。

はじめに

この記事はフィヨルドブートキャンプ Part 2 Advent Calendar 2022 - Adventar の16日目の記事です。

昨日は はるまきさんの 健康的に学習を続けるための肩こり奮闘記 - はるまきのブログでした。 エンジニアは肩こりとか腰痛になりやすそうなので、予防するのは大切ですね。

私がフィヨルドブートキャンプ(以下FBCと記載)に参加したのは2021年12月18日なので、約1年が経ちました。

そこで、この1年の振り返り記事を書いていくことにします。 現在FBCへの参加を検討されている方や既にFBCに参加されている方に、どんな感じで学習が進んでいくのかの一例として参考になればと思います。

参考までに、私の学習時間は9月まで平均6時間くらい、9月以降は3〜5時間くらいです。

FBC参加のきっかけ

エンジニアになりたいと思い独学でPythonの学習をしてましたが、オブジェクト指向の学習方法がわからず、またポートフォリオもどんなものを作ればいいのかわからないため独学に限界を感じ、プログラミングスクールを探すことにしました。

いろいろなスクールについて調べましたが、以下の理由でFBCにしようと思いました。

1.現役エンジニアの方からフィードバックがもらえる

やはり業務でプログラミングをされている方からフィードバックがもらえるというのは大きいです。現役のエンジニアの方がメンターとなり、提出物を見てレビューを下さったり、質問があれば答えてもらえます。

2.月額制なので、やっぱり合わないってなった時の負担が少ない

月額約3万円で入会金などもないので、やっぱりスクールと合わないとか、エンジニアに適正がないとかがわかった時に負担が少ないのがいいと思いました。

3.カリキュラムでオブジェクト指向が学べる

独学を挫折した要因でもあるので、カリキュラムにオブジェクト指向があるのがいいと思いました。

4.アンチハラスメントポリシーがある

アンチハラスメントポリシー | FJORD BOOT CAMP(フィヨルドブートキャンプ)を掲げ、快適なコミュニティを目指しているのがいいなと思いました。

5.年齢制限が無い

他のスクールだと20代限定とかの年齢制限があるところも多いので、年齢制限がないのは決め手の一つになりました。私が知る限りFBCには10代の学生の方から50代の方までいらっしゃいます。

2021年12月:基礎学習

基本的なことをプラクティスを通して学習していきます。プラクティスというのは学習内容がまとまってるカリキュラムのようなものです。 FBCの活動は、主にFBCのWebアプリとDiscordという音声チャットツールを通して行います。

まずはブログやGithubアカウントの開設からはじまり、学習用マシンの環境構築、HTMLやCSSなども学習します。VPSサーバーを契約してLinuxの学習もするのですが、このsshログインをするのが結構苦労しました。

GithubとかLinuxの学習は今なら大切さがわかりますが、最初は嫌で仕方ありませんでした。 Windowsとかのアイコンでの操作に慣れているので、あの素っ気ない黒い画面での操作が辛かったです。

かなり苦労しましたが上手く公開鍵でのログインができた時は嬉しくて、その後ここのプラクティスで苦労してる人を日報やQ&A掲示板で見つけては、「もしかしてこれが原因で詰まっているのでは?」と突撃してました(お節介かもですが)。

独学では絶対にやってなかったと思うのでFBCに参加して本当に良かったと思います。

2022年1月:Rubyの学習

ここは主にプロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで Software Design plus (以降チェリー本)から学ぶことになります。 プラクティスの課題で使うことはこの本かRubyリファレンスマニュアル を見れば大体書いてあります。

課題ではカレンダーやボウリングのスコア計算、LSコマンドやWCコマンドを自分で作ります。

中でもボウリングのスコア計算のロジックを考えるのが楽しかったのが印象に残っています。

2月:再びLinux、データベース

Nginxの学習とPostgreSQLの学習で再びLinuxを触ります。 Linuxにも慣れてくるので最初ほど嫌な感じはしないです。

データベース操作は必読書籍にはなっていないものの、SQL 第2版 ゼロからはじめるデータベース操作も読んで実際に操作しながら理解してました。

このプラクティスが終わったらVPSサーバーは解約してしまって大丈夫みたいです。

3月:Sinatra&Railsで挫折しそうになる

自作サービス制作を除けば、プラクティスで一番苦労したのはSinatraのメモアプリ作りでした。

Sinatraは初めてのフレームワークで、使い方が全然わからず苦労しました。 当時は検索しても情報が少なく、公式ドキュメントも個人的には読みづらかったので困りました。あまりに理解できていなくて、HTTPリクエストでDELETEを送ったら一体何が消えるんだろう?と怖々してました。 何も処理を書いていないので、当然何も消えませんでした。(自分で処理を書かないといけないことも理解できていなかった。)

仕方ないので先にRailsのプラクティスを最初の方だけやって、WEBアプリケーションについての基本的な動きを理解することでなんとか先に進めました。

そのRailsも最初は結構辛かったです。バランバランにファイルが分かれていて何がどこにあるの?と絶望的な気分になりました。

これはプラクティスの参考書籍にもある独習Ruby on Railsをしっかり読むと基本的なことが理解できました。他のRails本も何冊か読みましたしRailsチュートリアルもやってみましたが、下手に情報を求めて手を広げるよりまずは独習Rails一冊に絞って学習した方がいいと思います。これは入門用として本当によく書かれているのでおすすめです。

4月:オブジェクト指向Javascript

オブジェクト指向は、前のRubyのプラクティスでもやったボウリングのスコア計算プログラムとLSコマンドをオブジェクト指向で作ります。 前に作ったプログラムをオブジェクト版で作るので、使う組み込みメソッドなどを一から調べなくて済むのは大きいと思いました。 今プラクティスをみたら参考書籍が更新されていて、進めやすくなっていそうですね。

Javascriptは非同期処理がよくわからず苦労しました。今もよく分かっているかというとちょっと自信がないので、まだまだ勉強したいと思います。

5月:npm作成、Vue

Javascriptを使ってnpmというパッケージを作り、公開するところまでやります。FBCで作られたnpmはいろんなものがあって面白いですね。診断系やミニゲーム、何かを表示するものや実用的なものもあります。

私はチンチロを作りました。もしよかったら遊んでみてください!

www.npmjs.com

あとVueも学習します。フレームワークは最初全然わからない!ってなりますが、もう3回目なのでそろそろわからないのにも慣れてきます。 しつこく公式ドキュメントとかVue.js入門 基礎から実践アプリケーション開発までを読んでディレクティブの挙動を試して理解できると面白くなってきました。

6月:チーム開発に参加

6月1日からチーム開発に参加させていただきました。チーム開発ミーティングはDiscordの専用チャンネルで毎週水曜に行われます。 昼の部(15:00〜)と夜の部(22:00〜)があり、どちらかに参加します。FBCでは基本的にカメラで顔を映す必要はないのですが、このミーティングだけは顔出し必須です。

私は本格的にDiscordを使うのもチーム開発に入ってからでした。それでも問題はないのですが、もったいないとは思います。

最初は画面共有のやり方もよくわからないし、結構真剣な雰囲気なため緊張して非常に疲れるので、終わったらぐたっとしてました。

しかし、チーム開発はやはり面白いです。実際のFBCのアプリに反映されるので、自分がこの機能を追加したんだ!という満足感があります。 レビューのやり取りも勉強になりますね。

7月:輪読会に初参加

チーム開発も一区切りし、技術評論社から出ているパーフェクト Ruby on Rails 【増補改訂版】の輪読会が7月25日から行われるということなので参加させていただくことにしました。

輪読会というのはプラクティスとは無関係にFBCの有志で行われている活動で、その名の通り本を1冊決めて輪読をします。 FBC参加後こういうコミュニティ活動に参加し始めたのはここからになります。(かなり遅い方だと思います)

  • 本は自分のペースで読みたい

  • みんなで輪読してもそんなに理解しやすくなるわけじゃないと思う

という思想の持ち主だったのですが、間違っていました! 輪読会に参加するメリットはかなり大きいです。 私が考えるメリット

習慣的に学習できる

輪読会によりますが、毎週○曜日とか平日毎日とか定期的に行われるので、習慣的に学習できます。

わからないところを話し合うことで理解しやすくなる

わからないことをみんなで話し合うとわかるようになることが結構あります。輪読会に集まってくる人は勉強熱心な人が多いので、その場ではわからなくても、後日調べて教えてくださることもありました。 また、なんとなく流していたところで、他の人が「ここがわからないんだけど」と言うのを聞いて、確かにどういうことだろう?と実は理解できていないことが意識化されることもよくあります。 とにかく、自分一人で学習するより遥かに理解しやすいです。

たまに参加してくださるメンターさんから色々教われる

例えばcommand + fでコードない検索できるよ〜とか、VSCodeにこのプラグイン入れておいた方がいいよ、とかそんな感じの本に書いていないことを教われました。

他の方からの情報が入ってくる

エンジニア界隈のいろいろな情報が自然と入ってきます。今度こういうカンファレンスがあるとか、この本がおすすめとか、あと既に働いていらっしゃる方も参加されていることがあるので、現場での技術の使われ方なども耳に入ってきます。

他のコミュニティ活動に参加しやすくなる

実はこれまでミートアップにも参加できなかったぐらいのビビりなのですが、輪読会で顔見知りができたことによりミートアップやLT会にも参加できるようになりました。

ついでにここで宣伝なのですが、来年2023年の1月から毎週土日の14:00〜15:00にリーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)の輪読会が開催される予定です。私も共同主催者として参加させていただく予定なので、FBCに参加されている方ご一緒に輪読しませんか?

詳細は以下をご覧ください。

リーダブルコード輪読会2023を企画・主催します!📘 - LEFログ:学習記録ノート

リーダブルコードは一応オブジェクト指向のプラクティスの参考書籍になってますが、Rubyのプラクティスに入ったぐらいでも十分理解できると思います。参加はしたい日だけで大丈夫ですし、初めてでも理解できる自信がなくても全く問題ないです。

ご参加お待ちしています!

8月:自作サービスを作り始める

何を作るかかなり前から考えていたにも関わらず、結局決まらずに空いた期間ができてしまいました。 私が考えつくようなものは大抵既にあるんですよね。 没案をいくつか載せると、

  1. 災害用備蓄品の管理アプリ → 既にありました

  2. トイレ検索アプリ → 流石にないだろうと思ったら既にありました

  3. おすすめ本紹介CGM → ユーザー数が必要で運営に自信が持てないので没

ネタ切れなので身近な人に「こういうアプリがあったらいいなというアイデアない?」と聞いて回ったら、勤務予定の自動調整アプリがいいということで、それを作ることにしました。自分で勤務予定を決められる労働者向けのニッチなサービスなので、競合はいなそうです。

エレベーターピッチというアプリの簡潔な説明をまとめた後、ペーパープロトタイプを作りました。 このペーパープロトタイプの作成に使うWEBサービスの候補がいくつかあるのですが、Figmaの方を使うことをお勧めします。 私はBalsamiqを使ったのですが、操作が簡単で使いやすい反面、1ヶ月は無料でそれ以降は課金しないと使えなくなるので後から修正したくなった時に困るかもしれません。(課金するつもりなら問題ないと思います)

9月〜10月:技術検証

主にGoogleカレンダーとの連携の技術検証をしてました。難しい上に終わりが見えなくてモチベーションがだだ下がり、学習時間も3時間くらいに落ちました。2ヶ月ぐらい検証して、やっぱり実現できないかもってなった時は本当にどうしようかと思いました。 一縷の望みをかけてQ&A掲示板で質問したらメンターの方から回答をいただけて、なんとか実現できそうとなり首の皮一枚で繋がりました。

輪読会はこの期間もほぼ毎日参加してました。卒業生の方にGoogleカレンダーのことを聞けたり、Railsの操作にも慣れたりしたので、輪読会への参加したのはこの時期を乗り越えるのに大きかったと思います。 FBCに入る時は全然重視していませんでしたが、コミュニティは学習の上でも本当に大事だと思います。

コミュニティ内のQ&A掲示板とかも知見の宝庫ですよ。

11月:Rails new

やっと検証を終えて自作アプリを作り始めました。既に満身創痍気味ですが、やはり実際に動くものを作り始めると楽しいものですね。 自作アプリは自分で技術構成を決めるのですが、私はRuby3.1 + Rails7 + Vue3というFBCで学んだ技術をフル活用する構成にしました。 Next.jsやReactなどのプラクティス外の技術を自分で学習して使っている方も多いです。

12月:現在

自作サービスの製作中です。今は勤務予定の自動調整の機能を作っていて、完成まではもう少し時間がかかりそうです。

後2ヶ月ぐらいで卒業できたらいいなと思っています。

終わりに

こんな感じです。 これはあくまで私の場合で人によって進度は全く違いますし比べる必要もないのですが、一例として参考になれば幸いです。

当初半年で卒業しよう!なんて考えていましたが、あっという間に1年が経ってしまいました。 進むのが本当に早い人でも卒業まで9ヶ月はかかっているので、期間としては1年くらいが目安になると思います。

明日はFBCを卒業して就職された ぴー さんが記事を書いてくださいます。 卒業した後、1年目はどんな感じで働いていくのか興味があるので楽しみです。

chinchiroのロジック解説(10)

親と子の勝負メソッド

前回少し触れたboutメソッドは、親と子が一対一で役の強さで勝負するメソッドです。

長いので、親が勝った場合だけ紹介します。子の勝ちの時はほぼこれをひっくり返すだけで、引き分けの時は状態の変化がないだけなので、説明は不要かと思います。

async bout (dealer, user) {
  console.log(`${user.name}: ${user.betting} bet`)
  await this.sleep(1000)
  await this.create_role(user)
  if ((dealer.role === -1) || (user.role === -1)) {
    user.betting = user.betting * 2
  }
  if (dealer.role > user.role) {
    const payout = user.betting * this.payout_rate(dealer.role)
    dealer._token += payout
    user._token -= payout
    console.log(`${dealer.name} win`)
    console.log(`${dealer.name}: ${dealer._token}(+${payout})`)
    console.log(`${user.name}: ${user._token}(-${payout})`)
  } 
}

まず、boutメソッドは引数を2つ取り、先の方が親番のユーザーのインスタンス、後の引数が親と勝負する子のユーザーのインスタンスです。

最初の方でcreate_role(user)で子の役を作ります。できた役はユーザーのインスタンスrole属性として持たせます。

親番の役は、このboutメソッドが呼び出される前に作って、やはり親のユーザーのインスタンスに持たせます。

なぜ親の役はこのboutメソッドで作らないかというと、chinchiroは1回のサイクルの中で、親が最初に作った役とそれぞれの子が勝負する形なためです。

次の if ((dealer.role === -1)...のところでは、親か子の役が-1の時、子の掛け金が2倍になるようになっています。

この-1は、役がヒフミの時の数字です。chinchiroでは、勝負するために役の強さを数値で持たせています。

最大がピンゾロの13で、次が6のゾロ目の12、5のゾロ目の11と続き、目無しが0、ヒフミが-1です。

ヒフミは最弱の役で、しかも相手に払う金額が2倍になってしまう負の役です。

賭け金を決めるのは子のため、子の賭け金の値が2倍になるようにしています。

親の持つ役の値と子の持つ役の値で比べて、親が多かった場合if (dealer.role > user.role) ...の処理を行います。

賭け金user.bettingと、payout_rate(dealer.role)で割り出した倍率を使い、払戻額payoutの変数を出して賭け金の移動を行います。

chinchiroのロジック解説(9)

イカサマ

今回はイカサマのロジックについて解説します。

if (action === 'ikasama') {
  if (this.users[0] !== this.user1) {
    await this.user1.bet()
  }
  this.jigoro_dice._lisk += 20
  if (this.jigoro_dice._lisk > 100) {
    this.jigoro_dice._lisk = 100
  }
  this.user1._dice = this.jigoro_dice
  if (Math.floor(Math.random() * 100) < this.jigoro_dice._lisk) {
    console.log('\u001b[91mdiscovered!\u001b[0m')
    this.user2._dice = this.pinzoro_dice
    this.user3._dice = this.pinzoro_dice
  } else {
    console.log('\u001b[92mnot discovered\u001b[0m')
  }
}

上から順に説明します。まず、アクションの選択でイカサマが選ばれた場合にこれらの処理が行われます。

users[0]がuser1でない時、つまりプレイヤーが親でない時に掛け金を決めます。親の時は子が掛け金を決めたのを受ける形のため、掛け金を決めるbet()メソッドを行いません。

また、イカサマがバレる判定の前に掛け金を決めないといけないので、最初の方にこの処理を持ってきています。

ジゴロさい

ロジック解説(1)でも書きましたが、ジゴロさいはプレイヤー専用のサイコロで、コンストラクタで_liskの属性をもっているオブジェクトです。

通常のサイコロと違い、4〜6の値しか出ないため有利にゲームを進められます。

class JigoroDice {
  constructor () {
    this._lisk = 0
  }

  get lisk () {
    return this._lisk
  }

  set lisk (probability) {
    this._lisk = probability
  }

  roll () {
    return Math.floor(Math.random() * 3) + 4
  }
}

ただし、イカサマのたびに発覚のリスクの数値が20%ずつ上がり、イカサマが発覚するとペナルティがあります。

if (Math.floor(Math.random() * 100) < this.jigoro_dice._lisk) 

この部分でランダムな0〜100未満の数値とリスクを比べ、リスクの数値の方が高い場合に発覚、としました。

見つかった場合その回に限り他のユーザーのサイコロが、必ずピンゾロが出るピンゾロさいになります。(漫画カイジを少し再現)

chinchiroのロジック解説(8)

パスの処理

今日はパスの処理について書きます。

chinchiroでは、while(true)で親を変えながらゲーム進行のイテレーターのサイクルを回すのがメイン処理だということを前回の記事に書きました。

プレイヤーが親の時と、子の時で処理が違うのでご紹介します。

プレイヤーが親の時

アクションの選択肢でパスを選ぶことにより、親を交代して次のサイクルに移るようになっています。

if ((action === 'pass') && (this.users[0] === this.user1)) {
  const prevDieler = this.users.shift()
  this.users.push(prevDieler)
  continue
} 

前の記事にも書きましたが、usersはユーザーインスタンスの入った配列で、user1はプレイヤーです。そしてusers[0]は親番を表しています。

アクションでパスが選ばれ、かつ親番がプレイヤーの時、users.shift()で先頭のユーザー(親)を配列から取り除き、users.pushで配列の最後に追加します。 そして、continueでその回のサイクルを抜け、次のサイクルに移ります。

プレイヤーが子の時

親と同じようにcontinueを使うと、プレイヤーと親の勝負だけでなく、プレイヤー以外のユーザー2人の勝負も飛ばされることになってしまいます。 そこで、子の時には親の時とは違うパスの処理をすることにしました。

if ((action !== 'pass') || (this.users[1] !== this.user1)) {
  await this.bout(this.users[0], this.users[1])
}
if ((action !== 'pass') || (this.users[2] !== this.user1)) {
  await this.bout(this.users[0], this.users[2])
}

bout()は、親と子のユーザーが勝負するメソッドです。 アクションがパスでない、または親と勝負するユーザーでない場合は勝負が行われます。 つまり逆にいうと、アクションがパスかつ親と勝負するユーザーの時は処理が行われない=パスと言うことになります。

こうすることで、パスの時でもプレイヤー以外のユーザーの勝負は行われ、プレイヤーの勝負だけ飛ばすことができるようなロジックになっています。

chinchiroのロジック解説(7)

ゲーム進行のロジック

今回はメインとなるゲーム進行のロジックについて解説します。

while (true) {
  色々な処理
}

基本的には、行動選択→掛け金の決定→各プレイヤーがサイコロを振って役を作る→勝敗を決定→掛け金の払い戻しを1サイクルとして、while(true)でこれをぐるぐると永久的に回していきます。

親番の交代

chinchiroではプレイヤーを含む各ユーザーのインスタンスを、イテレーターの外の配列usersに入れています。

この配列の先頭、つまりusers[0]を親番としてゲーム進行が行われるようにしてあります。

そして、サイクルの最後でこのusers[0]を配列から削除し、再び配列に追加します。

こうすることで、例えば[user1, user2, user3]だったとすると、[user2, user3, user1]のように順番が入れ替わり、次のサイクルの親が1つずれて交代で行われるようになります。

ゲームセット

誰かの所持金が0以下になるとgamesetメソッドが作動し、ゲームが終わります。

これ以外ではアクションの選択でsurrenderを選んだ場合もゲームを終わらせることができます。

  gameset () {
    console.log('\u001b[1mgameset!\u001b[0m')
    this.users.sort(function (a, b) {
      if (a._token < b._token) return 1
      if (a._token > b._token) return -1
      return 0
    })
    for (const user of this.users) {
      console.log(`${user.name}: ${user._token}`)
    }
    if (this.users[0] === this.user1) {
      console.log('☆☆☆\u001b[3myou are winner!\u001b[0m☆☆☆')
    }
    process.exit()
  }

前述の通りusersはユーザーのインスタンスが入った配列で、this.users.sortメソッドで所持金の多い順に並び替えてから表示しています。これが最終順位になります。

これがゲーム開始から終了まで一連の流れです。

chinchiroのロジック解説(6)

ansi-escape-sequencesの使い方

前回はサイコロを振る時に、chin chiro rinの文字が画面に表示されて消える演出について解説しました。

その時にansi-escape-sequencesというnpmを使ったので、今回はansi-escape-sequencesを使った部分を紹介します。

強調

console.log("\u001b[1m強調したいテキスト\u001b[0m") \u001b[1mで文字の強調開始、\u001b[0mで文字モードを元に戻しています。 同じような文字列が縦に並ぶとどうしても見辛い上、ゲーム進行で文字がどんどん上に流れていくのでどこまで読んだのかわからなくなりがちです。

そこで、chinchiroでは特に重要と思われる情報(誰の親番かなど)を強調することで、文字が流れていっても基準になるように考えました。

色をつけることもできます。 chinchiroではイカサマが見つかるかそうでないかで色を変えています。

console.log('\u001b[92mnot discovered\u001b[0m')

console.log('\u001b[91mdiscovered!\u001b[0m')

イカサマが見つからなかった場合は安心の緑(\u001b[92m)

見つかってしまった場合、警告の赤(\u001b[91m)に文字色を変えることで緊張感を演出しようと考えました。

また、サイコロの一の目も赤にしています。(上の赤と少し色味が違うので、末尾が[31mになっています)

const diceAA2 = { 1: '  |   \u001b[31m●\u001b[0m   |'}

終わりに

ansi-escape-sequencesというnpmは、AAにパラパラ漫画的な動きをつけたり、文字に色や効果をつけたりできる便利なnpmです。

ぜひ使ってみて、さまざまな表現をしてみてください!

公式ページで他にも色々な使い方が書いてあります。(英語) www.npmjs.com