---
title: JPPGB#5_おいしみ_Power AppsでYavalath作ってみた！
tags:  #powerapps  
author: [おいしみ](https://convert.docswell.com/user/ksgiksg)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/LE1MV3227G.jpg?width=480
description: イベント(https://jppgb.connpass.com/event/280997/ )での登壇資料です。
published: May 19, 23
canonical: https://convert.docswell.com/s/ksgiksg/5JL9P3-2023-05-19-231004
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/LE1MV3227G.jpg)

Power Appsで
Yavalath作ってみた！
JPPGB #5


# Page. 2

![Page Image](https://bcdn.docswell.com/page/GEW3LPR2E2.jpg)

自己紹介
{
“DisplayName” : “おいしみ”,
“ID” : “ksgiksg”,
“Introduce” : [
おいしみ
“今年から情シス１年生”,
“前職は「プラントエンジニア」と、非IT職”,
“社内ではPower PlatformをはじめDX推進の講師役として活動中”,
“Qiitaでアウトプット始めてみました(主にPower Automate関連)”
],
“ContentURL” : [
“Twitter” : “https://twitter.com/ksgiksg”,
@ksgiksg
“Qiita” : “https://qiita.com/ksgiksg”
]
}


# Page. 3

![Page Image](https://bcdn.docswell.com/page/47ZVMKR4E3.jpg)

ヤバラス(Yavalath)ってどんなゲーム？


# Page. 4

![Page Image](https://bcdn.docswell.com/page/YJ65NQRG7V.jpg)

なんでPower Appsで作ったの？
• 日本での入手が困難だった
• 転職前有給消化中のタイミングで時間があった
• Power Apps で凝ったものを作ったことがなかった
せっかくだからチャレンジしてみよう💪
※習作をそのまま公開しています。
「なんでこうなってるの？」「こうした方がよかったんじゃない？」
など、ツッコミ大歓迎です！！
…というかリファクタリングしたかったのが間に合いませんでした😭


# Page. 5

![Page Image](https://bcdn.docswell.com/page/GJ5Y5R8XJ4.jpg)

デモ！


# Page. 6

![Page Image](https://bcdn.docswell.com/page/LE35NP84J5.jpg)

盤面の作成


# Page. 7

![Page Image](https://bcdn.docswell.com/page/8EDDWLM5EG.jpg)

作成したい盤面
盤面は左図のように
正六角形が規則的に並んだ
ハニカム構造の盤面を作成します。


# Page. 8

![Page Image](https://bcdn.docswell.com/page/V7PX92RDE8.jpg)

まずは１つ正六角形を作成する
Power Apps の図形コントロールには
正六角形がありません。
なので、何かしら自前で用意する必要があります。
今回は色・大きさ・線の太さなど自由度が効く
SVGで作成することにしました！
参考にさせていただいた記事
Power Apps で SVGを利用したレーダーチャートコンポー
ネントの作成 - MoreBeerMorePower (hatenablog.com)


# Page. 9

![Page Image](https://bcdn.docswell.com/page/2JVLMDRG7Q.jpg)

まずは１つ正六角形を作成する
メディアコントロールから画像(Image)を追加し
“Image”プロパティに以下の式を追加します。


# Page. 10

![Page Image](https://bcdn.docswell.com/page/5EGXGY6DJL.jpg)

まずは１つ正六角形を作成する
ポイントはこの部分。Nはここでは正六角形の6なので、2*Ｐｉ（）/N
つまり、度数法でいうと６０°ずつ回転させながら頂点を描画しています。
Concat(
Sequence(N),
６０°
A*Cos(2*Pi()*Value/N)
&amp;&quot;, &quot;
&amp;A*Sin(2*Pi()*Value/N),
&quot;&quot;
)
sinΘ
cosΘ


# Page. 11

![Page Image](https://bcdn.docswell.com/page/4JQ8XM4X7P.jpg)

ギャラリーで並べて配置する
次に、１マス１マスをテーブルの１行としてデータを作成し、
ギャラリーを使ってマスを並べていきます。
自由にアイテムを配置するには
TemplateSize=0の手法 を使用します。
参考にさせていただいた記事
わかれば簡単！ #PowerApps のTemplateSize=0の手法
- Qiita


# Page. 12

![Page Image](https://bcdn.docswell.com/page/K7426VQ2J1.jpg)

ギャラリーで並べて配置する
正方形であれば縦横のX,Y座標を順に並べていくだけでよいのですが、
正六角形ではそう単純ではありません。
X
X
(X,Y)=(4,2)
Y
(X,Y)=???
Y


# Page. 13

![Page Image](https://bcdn.docswell.com/page/LJ1MV3PKEG.jpg)

ギャラリーで並べて配置する
のちに、勝利判定を行うための向きの取り方が３方向あるので
マスの並べ方もこれに合わせ３方向のベクトルで表現します。


# Page. 14

![Page Image](https://bcdn.docswell.com/page/GJW3LP2PJ2.jpg)

3方向のベクトルで配置を決める
ということで、テーブルデータは連番のidとx,y,z座標を定義して
以下のようになります。(手入力です。。。要改善点。。。)


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4EZVMKD6J3.jpg)

3方向のベクトルで配置を決める
テーブルデータの座標用のプロパティ(x,y,z)から、
ギャラリーの中に正六角形を敷き詰めて並べます。
y
(X,Y,Z)=(0,0,0)
X
z
x
単位ベクトル
X,Y座標系
՜
𝑥
(1Τ2 , 1Τ2)
՜
(0, −1)
՜
(− 1Τ2 , 1Τ2)
𝑦
𝑧
Y


# Page. 16

![Page Image](https://bcdn.docswell.com/page/Y765NQ1LEV.jpg)

3方向のベクトルで配置を決める
正六角形のX,Yプロパティはそれぞれ次の通りです。
X
Self.Width*(3^0.5)/2*(
Y
Self.Height/2*(
ThisItem.x*(1/2)
ThisItem.x*(1/2)
+ThisItem.y*0
+ThisItem.y*(-1)
+ThisItem.z*(-1/2)
+ThisItem.z*(1/2)
)
)
+(G_Stage.Width-Self.Width)/2
+(G_Stage.Height-Self.Height)/2
-ThisItem.id


# Page. 17

![Page Image](https://bcdn.docswell.com/page/G75Y5R4M74.jpg)

駒の置き方


# Page. 18

![Page Image](https://bcdn.docswell.com/page/9J2GNDVRJR.jpg)

駒の用意
駒は初めからギャラリーに追加しておきます。
これを、表示/非表示を切り替えたり、色を変えたりして
駒を置くという動作を表現します。


# Page. 19

![Page Image](https://bcdn.docswell.com/page/DEYVP235EM.jpg)

駒の塗りつぶし
駒の塗りつぶし Fillプロパティは、プレイヤーごとに定義しています。
初期状態をP:0とし、１Pが駒を置いたらP:１に更新するようにしています。
Fill
With(
LookUp(colors,id=ThisItem.P) As color,
RGBA(color.R,color.G,color.B,color.A)
)


# Page. 20

![Page Image](https://bcdn.docswell.com/page/VJNM58V478.jpg)

駒の塗りつぶし
同様に、HoverFillも定義しています。
HoverFill
If(
ThisItem.P=0,
With(
LookUp(colors,id=WhoseTurn) As color,
RGBA(color.R,color.G,color.B,0.5)
),
Self.Fill
)


# Page. 21

![Page Image](https://bcdn.docswell.com/page/YE9NYK64E3.jpg)

ターン数の制御


# Page. 22

![Page Image](https://bcdn.docswell.com/page/GE8K6RZV7D.jpg)

ターン数の制御（途中抜け無し）
ターン数を１ずつ増やしていき、プレイ人数で割った余りを使えば、
そのターンがどのプレイヤーの手番かを判断することができます。
ターン数
1
2
Mod 2
1
0
2人プレイ時の手番
1P
2P
Mod 3
1
2
3人プレイ時の手番
1P
2P
3
4
5
1
0
1
1P
2P
1P
0
1
2
3P
1P
2P
6
0
2P
0
3P
…
…
…
…
…


# Page. 23

![Page Image](https://bcdn.docswell.com/page/LELZ9KDRJR.jpg)

ターン数の制御（途中抜けあり）
このゲームの場合、特に３人プレイの場合は、途中失格がありえるため
その後は手番がずれてしまいます。
ターン数
1
2
Mod 2
1
0
2人プレイ時の手番
1P
2P
Mod 3
1
2
3
4
5
1
0
1
1P
2P
1P
0
1
2
6
0
2P
0
…
…
…
…
3人プレイ時の手番
1P
2P❌失格
3P
1P
2P3P
3P1P
…


# Page. 24

![Page Image](https://bcdn.docswell.com/page/4JMVZ2PPJW.jpg)

ポインタ付きのリスト構造で表現
プレイヤーに、自分の次のプレイヤーを覚えさせることにしました。
プレイヤー：1P
2P
プレイヤー：2P
3P
プレイヤー：3P
1P
プレイヤー：1P
2P
プレイヤー：2P
3P
プレイヤー：3P
1P
プレイヤー：1P
2P
プレイヤー：2P
3P
プレイヤー：3P
1P
プレイヤー：1P
3P
プレイヤー：3P
1P
プレイヤー：2P
3P


# Page. 25

![Page Image](https://bcdn.docswell.com/page/PJR26M1YE9.jpg)

プレイ順の初期設定
ForAll(
Sequence(Dr_UserCount.Selected.No),
Collect(
PlayerList,
{
No:Value,
Status:&quot;プレイ中&quot;,
Next:Mod(Value,Dr_UserCount.Selected.No)+1
}
)
)


# Page. 26

![Page Image](https://bcdn.docswell.com/page/PEXDMVY47X.jpg)

失格＆ポインタの修正処理
//失格にしてプレイ順を修正
Patch(
PlayerList,
LookUp(PlayerList,No=WhoseTurn),
{Status:&quot;失格&quot;}
);
UpdateIf(
PlayerList,
Next=WhoseTurn,
{Next:LookUp(PlayerList,No=WhoseTurn).Next}
);


# Page. 27

![Page Image](https://bcdn.docswell.com/page/3EKXGQ3P7D.jpg)

ターン終了時の判定


# Page. 28

![Page Image](https://bcdn.docswell.com/page/L735NPG4E5.jpg)

処理の流れ
ぐちゃぐちゃでゴメン…
盤面クリック
No
3連のマス
はあるか？
(∵)
空白のマス？
Yes
プレイヤーを
失格
Yes
終了
No
マスを塗る
Nextプレイ
ヤーを更新
４連のマス
はあるか？
No
Yes
残り人数は
１人か？
Yes
勝利
No
手番を更新
次のターンへ


# Page. 29

![Page Image](https://bcdn.docswell.com/page/87DDWL157G.jpg)

処理の流れ
盤面クリック
No
パワポでフロー図,書きにくくない？
3連のマス
はあるか？
(∵)
空白のマス？
Yes
プレイヤーを
失格
Yes
終了
No
マスを塗る
Nextプレイ
ヤーを更新
４連のマス
はあるか？
No
Yes
残り人数は
１人か？
Yes
勝利
はじめはこの処理すべてを
GallaryのOnSelectに入れようとして
No
無残に散りました。
手番を更新
次のターンへ


# Page. 30

![Page Image](https://bcdn.docswell.com/page/VJPX92QD78.jpg)

処理ごとに切り出してボタンコントロールに
子フローを作成するようなイメージで
処理の塊ごとにボタンコントロールを作成し、
Select(ボタン)で子フローを呼び出すようにして処理を分割しました。
いい方法があれば教えて
(∵)


# Page. 31

![Page Image](https://bcdn.docswell.com/page/2EVLMD5GJQ.jpg)

勝利(４目並べ)の判定


# Page. 32

![Page Image](https://bcdn.docswell.com/page/57GXGYPDEL.jpg)

確認のための下処理
前述のとおり、３方向から確認する必要があります。
下処理として、駒の並びをテキストに変換します。
10201231


# Page. 33

![Page Image](https://bcdn.docswell.com/page/4EQ8XM2XJP.jpg)

確認のための下処理
Clear(tb_judge);
ForAll(
Sequence(9,-4) As S,
Collect(
tb_judge,
{
x:Concat(Sort(Filter(tb,x=S.Value),y),P&amp;&quot;&quot;),
y:Concat(Sort(Filter(tb,y=S.Value),z),P&amp;&quot;&quot;),
z:Concat(Sort(Filter(tb,z=S.Value),x),P&amp;&quot;&quot;)
}
)
);


# Page. 34

![Page Image](https://bcdn.docswell.com/page/KJ426VL2E1.jpg)

確認のための下処理
Clear(tb_judge);
ForAll(
Sequence(9,-4) As S,
Collect(
tb_judge,
{
x:Concat(Sort(Filter(tb,x=S.Value),y),P&amp;&quot;&quot;),
y:Concat(Sort(Filter(tb,y=S.Value),z),P&amp;&quot;&quot;),
z:Concat(Sort(Filter(tb,z=S.Value),x),P&amp;&quot;&quot;)
}
)
);


# Page. 35

![Page Image](https://bcdn.docswell.com/page/LE1MV3LK7G.jpg)

確認のための下処理
• Sequence(9,-4) As S
y
4
• y:Concat(Sort(Filter(tb,y=S.Value),z),P&amp;&quot;&quot;)
Sequence
Filter
3
2
1
0
1
3
1
0
2
4
0
1
2
-1
-2
-3
-4
z
4
3
2
1
0
-1
-2
-3
-4
Sort
Concat
101324012


# Page. 36

![Page Image](https://bcdn.docswell.com/page/GEW3LPVPE2.jpg)

４目並べの判定
続いて、同じ数字が４連続で並んでいるレコードがないか確認します。
!IsEmpty(
Filter(
tb_judge,
Or(
Find(Concat(Sequence(4),WhoseTurn&amp;&quot;&quot;),x)&gt;0,
Find(Concat(Sequence(4),WhoseTurn&amp;&quot;&quot;),y)&gt;0,
Find(Concat(Sequence(4),WhoseTurn&amp;&quot;&quot;),z)&gt;0
)
)
)


# Page. 37

![Page Image](https://bcdn.docswell.com/page/47ZVMK46E3.jpg)

失格(3目並べ)の判定


# Page. 38

![Page Image](https://bcdn.docswell.com/page/YJ65NQ3L7V.jpg)

３目並べの判定
判定は４目と同じ方法で判定します。
!IsEmpty(
Filter(
tb_judge,
Or(
Find(Concat(Sequence(3),WhoseTurn&amp;&quot;&quot;),x)&gt;0,
Find(Concat(Sequence(3),WhoseTurn&amp;&quot;&quot;),y)&gt;0,
Find(Concat(Sequence(3),WhoseTurn&amp;&quot;&quot;),z)&gt;0
)
)
)


# Page. 39

![Page Image](https://bcdn.docswell.com/page/GJ5Y5RLMJ4.jpg)

失格処理＆残りの人数確認
３目並んで、失格者が出た場合は以下の処理が続きます。
//失格にしてプレイ順を修正(前述のため省略)
//1人を残して失格ならゲーム終了
With({W:Filter(PlayerList,Status=&quot;プレイ中&quot;)},
If(
CountRows(W)=1,
Set(Winner,First(W).No);
Set(IsGamePlaying,false);
Patch(PlayerList,First(W),{Status:&quot;勝利&quot;}) //勝者決定・ゲーム終了
)
)


# Page. 40

![Page Image](https://bcdn.docswell.com/page/9E2GNDLRER.jpg)

今後の課題


# Page. 41

![Page Image](https://bcdn.docswell.com/page/D7YVP2Q57M.jpg)

発表スライド作って改めて感じたこと
• コントロール名や変数名が適当でつらかった
• 盤面の作成で手入力した座標は、数学的に何とかできそう
• 「１１０１」などのリーチ状態を可視化させたい
• ゲームは遊んでて楽しいことが一番なので見た目にもこだわりたい
• できれば１画面じゃなくオンラインで遊べるようにしたい


