ãã¹ãŠã®ããã°ã©ããŒãé¢çœãã²ãŒã ãäœããããšæã£ãç¬éããããŸããã å€ãã®ããã°ã©ããŒã¯ãããã®å€¢ãå®çŸããããã«ã¯æåããŸãããããã¯åœŒãã«é¢ãããã®ã§ã¯ãããŸããã ããã¯ãã²ãŒã ããã¬ã€ããã®ã奜ããªäººãç¥èãçµéšããªããŠãäžåºŠã²ãŒã ãäœæããããšããŠãäžççãªå声ïŒããã³å€§ããªå©çïŒãéæããå€ç¬ãªããŒããŒã®äŸã«è§Šçºããããã圌ã¯äœè£ããªãã°ã«ã€ã°ãã¹ããã€ãšç«¶ããŸãã
ããªãã§ãã ãã...
å°ããªç޹ä»
ããã«äºçŽããŸããç§ãã¡ã®ç®æšã¯ãéã皌ãããšã§ã¯ãããŸãã-ãã®ãããã¯ã«é¢ããå€ãã®èšäºãHabréã«ãããŸãã ãããã倢ã®ã²ãŒã ãäœããŸãã
倢ã®ã²ãŒã ã«ã€ããŠã®åæ
çãªäœè«ç¬èº«ã®éçºè
ãå°ããªã¹ã¿ãžãªãããã®èšèãäœåèããããšããããŸãã ã©ãã«ããŠããåå¿è
ã®ã€ã°ããããã¯åœŒãã®å€¢ãšãå®ç§ãªããžã§ã³ããæ¥ãã§äžçã«å
¬éãã圌ãã®è±éçãªåªåãä»äºã®ããã»ã¹ãé¿ããããªãçµæžçå°é£ãåºç瀟ãšäžè¬çãªããã¬ãŒã€ãŒ-æ©æµã®ãªãç¬-ã€ã -ã°ã©ããšã³ã€ã³ãšãã¹ãŠãç¡æã§æ¯æããã²ãŒã ãããªãã§ãã ãã-æµ·è³-ãããŠãç§ãã¡ã¯-圌ãã®ãããã§å€±ãããå©ç-ããã ãã§ãã
人ã
ãã ãŸãããŠã¯ãããŸããã ããªãã¯å€¢ã®ã²ãŒã ãäœã£ãŠããã®ã§ã¯ãªãããã売ããã²ãŒã ãäœã£ãŠããŸã-ãããã¯2ã€ã®ç°ãªããã®ã§ãã ãã¬ã€ã€ãŒïŒç¹ã«æŽç·Žããããã¬ã€ã€ãŒïŒã¯ããªãã®å€¢ãæ°ã«ããŸããã å©çãå¿
èŠãªå Žåããã¬ã³ããç ç©¶ããçŸåšäººæ°ã®ãããã®ã確èªãããŠããŒã¯ãªããšãããä»ã®äººãããåªãããããçããããšãè¡ããèšäºãèªã¿ïŒå€ããããŸãïŒãåºç瀟ãšã³ãã¥ãã±ãŒã·ã§ã³ãåããŸã-äžè¬ã«ãããªãã§ã¯ãªããšã³ããŠãŒã¶ãŒã®å€¢ãå®çŸããŸãã
ãŸã éããŠããªãã®ã«å€¢ã®ã²ãŒã ãå®çŸãããå Žåã¯ãäºåã«å©çãæŸæ£ããŠãã ããã 倢ã売ãã®ã§ã¯ãªããç¡æã§å
±æããŸãããã 人ã
ã«ããªãã®å€¢ãäžãã圌ãã«ããããããããããªãã®å€¢ãäœã䟡å€ããããªããããªãã¯ãéã§ã¯ãªããŠããæãšèªèãåãåããŸãã ããã¯æã«ã¯ã¯ããã«äŸ¡å€ããããŸãã
å€ãã®äººã¯ãã²ãŒã ã¯æéãšåŽåã®ç¡é§ã§ãããçé¢ç®ãªäººã¯ãã®ãããã¯ã«ã€ããŠãŸã£ãã話ãã¹ãã§ã¯ãªããšèããŠããŸãã ããããããã«éãŸã£ã人ã
ã¯æ·±å»ã§ã¯ãªãã®ã§ãç§ãã¡ã¯éšåçã«åæããŸã-ããªãããããããã¬ã€ããå Žåãã²ãŒã ã¯æ¬åœã«å€ãã®æéãããããŸãã ãã ããã²ãŒã ã®éçºã«ã¯äœåãæéãããããŸãããå€ãã®ã¡ãªããããããŸãã ããšãã°ãã²ãŒã 以å€ã®ã¢ããªã±ãŒã·ã§ã³ã®éçºã«ã¯èŠãããªãååãã¢ãããŒããã¢ã«ãŽãªãºã ã«æ
£ããããšãã§ããŸãã ãŸãã¯ãããŒã«ïŒããšãã°ãããã°ã©ãã³ã°èšèªïŒãææããã¹ãã«ãæ·±ããç°åžžã§åºæ¿çãªããšãè¡ããŸãã èªåã§ã²ãŒã éçºïŒæåããªããŠãïŒã¯åžžã«ç¹å¥ã§æ¯é¡ã®ãªãäœéšã§ãããšä»ãå ããŸãïŒãããŠå€ãã®äººãåæããŸãïŒããããææãšæã§æãåºããŸãã
ç§ãã¡ã¯ãææ°ã®ã²ãŒã ãšã³ãžã³ããã¬ãŒã ã¯ãŒã¯ãã©ã€ãã©ãªã䜿çšããŸãããã²ãŒã ãã¬ã€ã®æ¬è³ªãèŠãŠãå
éšããæããŸãã æè»ãªéçºæ¹æ³è«ã¯æŸæ£ããŸãïŒ1人ã®äœæ¥ãæŽçããå¿
èŠããããããã¿ã¹ã¯ã¯ç°¡çŽ åãããŸãïŒã ãµãŠã³ãã®ãã¶ã€ããŒãã¢ãŒãã£ã¹ããäœæ²å®¶ãã¹ãã·ã£ãªã¹ããæ¢ãããã«æéãšãšãã«ã®ãŒãè²»ããããšã¯ãããŸãã-ç§ãã¡ã¯ã§ããéãã®ããšããã¹ãŠããŸã宿ãããã¬ãŒã äžã®ã°ã©ãã£ãã¯ïŒã æçµçã«ã¯ãããŒã«ãå®éã«ç ç©¶ããŠé©åãªããŒã«ãéžæããããšããããŸãããäœ¿çšæ¹æ³ãããã£ãŠãããã®ã§å®è¡ããŸãã ããšãã°ãJavaã®å ŽåãåŸã§å¿
èŠã«å¿ããŠAndroidïŒãŸãã¯ã³ãŒããŒã¡ãŒã«ãŒïŒã«è»¢éããŸãã
ããã!!! ãã©ãŒïŒ æªå€¢ïŒ ã©ãããŠãããªãã³ã»ã³ã¹ã«æéãè²»ããããšãã§ããŸããïŒ ããããåºãŠããã£ãšé¢çœããã®ãèªãã§ãããŸãããïŒãããã¯ãªãã§ããïŒ ã€ãŸããè»èŒªãåçºæããŸããïŒ æ¢è£œã®ã²ãŒã ãšã³ãžã³ã䜿çšããŠã¿ãŸãããïŒ çãã¯ç°¡åã§ãã圌ã«ã€ããŠã¯äœãç¥ããŸããããä»ã¯ã²ãŒã ãæ¬²ããã§ãã å¹³åçãªããã°ã©ããŒã®èãæ¹ãæ³åããŠãã ããããç§ã¯ã²ãŒã ãäœãããïŒ èãççºããã³ããããã
ã³ããããŒã奪ãããšãã§ã ãããããã¯çæãããããã¯ä»ã®ã©ãã§ãèµ·ãã£ãããšã¯ãããŸããïŒ ç§ã¯ä»ããæžãå§ããŸãïŒ ä»ãç§ãã¡ã«äººæ°ã®ãããã®ãèŠãŠã¿ãŸããã...ãããXãYãZãZãåãã誰ãã仿žããŠããŸã...ãã ãããŠããšã³ãžã³ã®ç ç©¶ãéå§ããŸãã ãããŠã圌ã¯ãã®ã¢ã€ãã¢ãæããŸãããªããªããããã«ã¯ãã§ã«ååãªæéããªãããã§ãã ãã£ã³ã ãŸãã¯ã倧äžå€«ãããã¯ãããããŸããããå®éã«ãšã³ãžã³ãåŠç¿ããããšãªããã²ãŒã ã«åãå
¥ããããŸãã ãŸãããã®åŸã圌ã¯èª°ã«ãåœŒã®æåã®ãå·¥èžåããèŠããªãè¯å¿ãæã£ãŠããŸãã éåžžã¯ããã§ã¯ãããŸããïŒã¢ããªã±ãŒã·ã§ã³ã¹ãã¢ã«è¡ã£ãŠãèªåã§ç¢ºèªããŠãã ããïŒ-ãŸãããŸããç§ã¯å©çãæã¿ãèããåã¯ãããŸããã ãã€ãŠã²ãŒã ã®äœæã¯ç±ççãªåµé çãªäººã
ã§ããã æ²ããããªããã®æéã¯åãè¿ãã®ã€ããªãã»ã©éããŸãã-ã²ãŒã ã®äž»ãªãã®ã¯éã§ã¯ãªããããžãã¹ã¢ãã«ã§ãïŒå°ãªããšãããã«ã€ããŠã®äŒè©±ã¯ãã£ãšãããããããŸãïŒã ç§ãã¡ã®ç®æšã¯ã·ã³ãã«ã§ããéã§ã²ãŒã ãäœããŸãã ãããã£ãŠãããŒã«ããæœè±¡åãïŒèª°ã§ãå®è¡ã§ããŸãïŒãã¿ã¹ã¯ã«éäžããŸãã
ããã§ã¯ãç¶ããŸãããã
ç§èªèº«ã®èŠãçµéšã®è©³çްã«ã¯è§ŠããŸããããã²ãŒã éçºã«ãããããã°ã©ããŒã®äž»ãªåé¡ã®1ã€ã¯ã°ã©ãã£ãã¯ã¹ã ãšèšããŸãã ããã°ã©ããŒã¯éåžžãæç»ã®æ¹æ³ãç¥ããïŒäŸå€ã¯ãããŸãïŒãã¢ãŒãã£ã¹ãã¯éåžžââãããã°ã©ãã³ã°ã®æ¹æ³ãç¥ããŸããïŒäŸå€ã¯ãããŸãïŒã ãããŠãã°ã©ãã£ãã¯ããªããã°ãããªãã¯èªããªããã°ãªããŸããããŸããªã²ãŒã ã¯ãã€ãã¹ãããŸãã ã©ãããïŒ
ãªãã·ã§ã³ããããŸãïŒ
1.ã·ã³ãã«ãªã°ã©ãã£ã«ã«ãšãã£ã¿ã§ãã¹ãŠãèªåã§æã
2003幎ã®ã²ãŒã ãKillââ Him Allãã®ã¹ã¯ãªãŒã³ã·ã§ãã 2.ãã¹ãŠãèªåã§ãã¯ãã«ã«æã
ã²ãŒã ãRavenãã2001幎ã®ã¹ã¯ãªãŒã³ã·ã§ãã
ã²ãŒã ãInfernoãã2002幎ã®ã¹ã¯ãªãŒã³ã·ã§ãã 3.æãæ¹ãããããªãå
åŒã«å°ããïŒããããå°ãè¯ãããïŒ
ã²ãŒã ãFuckingãã2004幎ã®ã¹ã¯ãªãŒã³ã·ã§ãã 4. 3Dã¢ããªã³ã°çšã®ããã°ã©ã ãããŠã³ããŒãããŠãããããã¢ã»ãããååŸããŸã
2006幎ã®ã²ãŒã ãFucking 2. Demoãã®ã¹ã¯ãªãŒã³ã·ã§ãã 5.çµ¶æçã«ãé ã®æ¯ãåŒãè£ã
ã²ãŒã ãFuckingãã2004幎ã®ã¹ã¯ãªãŒã³ã·ã§ãã 6.èªåã§ãã¹ãŠãæ¬äŒŒã°ã©ãã£ãã¯ã¹ïŒASCIIïŒã§æç»ããŸã
ã²ãŒã ãFifaãã®ã¹ã¯ãªãŒã³ã·ã§ããã2000
1998幎ã®ã²ãŒã ãçžæ²ãã®ã¹ã¯ãªãŒã³ã·ã§ãã åŸè
ã«ã€ããŠè©³ããèŠãŠãããŸãããïŒäžéšã¯ä»ã®äººã»ã©æ°ã®ãããããã«èŠããªãããã§ãïŒã çµéšã®æµ
ãã²ãŒããŒã®å€ãã¯ãã¯ãŒã«ã§ã¢ãã³ãªã°ã©ãã£ãã¯ã®ãªãã²ãŒã ã¯ãã¬ã€ã€ãŒã®å¿ãã€ããããšãã§ããªããšèããŠããŸããã²ãŒã ã®ååã§ããã²ãŒã ã«å€ããããšããã§ããŸããã
ADOM ã
NetHack ã
Dwarf Fortressãªã©ã®åäœã®éçºè
ã¯ããã®ãããªè°è«ã«æé»çã«å察ããŠããŸãã å€èгã¯åžžã«æ±ºå®çãªèŠå ã§ã¯ãããŸãã
ãASCIIã䜿çšãããšãããã€ãã®è峿·±ãå©ç¹ãåŸãããŸãã
- éçºã®éçšã§ãããã°ã©ããŒã¯ã²ãŒã ãã¬ã€ãã²ãŒã ã®ä»çµã¿ãããããã³ã³ããŒãã³ããªã©ã«çŠç¹ãåœãŠãäºçްãªããšã«æ°ãæ£ããããšãªã;
- ã°ã©ãã£ãã¯ã³ã³ããŒãã³ãã®éçºã«ã¯ããŸãæéãããããŸãããåäœãããããã¿ã€ãïŒã€ãŸããçè§£ã§ãããã¬ã€ããããŒãžã§ã³ã§ãããç¶ãã䟡å€ã¯ãããŸãïŒã¯ãã¯ããã«æ©ãæºåã§ããŸãã
- ãã¬ãŒã ã¯ãŒã¯ãšã°ã©ãã£ãã¯ãšã³ãžã³ãåŠã¶å¿
èŠã¯ãããŸããã
- ã²ãŒã ãéçºãã5幎éã§ã°ã©ãã£ãã¯ã¹ãé³è
åããããšã¯ãããŸããã
- çéå
¥ãã®åŽåè
ã¯ãã°ã©ãã£ã«ã«ç°å¢ãæããªããã©ãããã©ãŒã äžã§ã補åãè©äŸ¡ã§ããŸãã
- ãã¹ãŠãæ£ããè¡ãããŠããã°ãã¯ãŒã«ãªã°ã©ãã£ãã¯ãåŸã§åºå®ã§ããŸãã
äžèšã®é·ã玹ä»ã¯ãåå¿è
ã®ã€ã°ãããããææãåèŠãå
æããå¿é
ããããããã§ããã®ãããªããšãããããšããã®ãå©ããããšãæå³ããŸããã æºåã¯ããïŒ ããã§ã¯å§ããŸãããã
æåã®ã¹ãããã ã¢ã€ãã¢
ã©ããã£ãŠïŒ ãŸã ããããªãïŒ
ã³ã³ãã¥ãŒã¿ãŒã®é»æºãåããé£ã¹ã«è¡ããæ£æ©ããéåããŸãã ãŸãã¯ææªã®å Žåãç ããŸãã ã²ãŒã ãæãä»ããšããããšã¯ãçªãæŽãããšã§ã¯ãããŸãã-ããã»ã¹ã®æŽå¯ã¯åŸãããŸããã éåžžãã²ãŒã ã®ã¢ã€ãã¢ã¯ããŸã£ããèããªããšãã«çªç¶ãäºæããã«çãŸããŸãã ãããçªç¶èµ·ãã£ãå Žåã¯ãéçãããéãã€ãã¿ãã¢ã€ãã¢ãæ¶ãããŸã§æžãçããŠãã ããã ã¯ãªãšã€ãã£ãããã»ã¹ã¯ãã¹ãŠãã®æ¹æ³ã§å®è£
ãããŸãã
ãããŠãä»ã®äººã®ã²ãŒã ãã³ããŒã§ããŸãã ãŸããã³ããŒã ãã¡ãããããªããã©ãã»ã©é ãããããé
ã
ãŸã§èšã£ãŠäžæ¬ã«æŠãã®ã§ã¯ãªããããªãã®è£œåã§ä»ã®äººã®éçºã䜿ã£ãŠãã ããã å€ãã®å Žåãã²ãŒããŒã¯ãããæã£ãŠããã®ã§ããããããªãã®å€¢ããå
·äœçã«ã©ã®ãããæ®ã£ãŠããã®ã§ããããïŒåœŒãã¯ããã€ãã®è¿·æãªããšãé€ããŠã²ãŒã ã®ãã¹ãŠã奜ãã§ããããããç°ãªã£ãŠè¡ãããå Žå...誰ãç¥ã£ãŠããŸã誰ãã®è¯ãã¢ã€ãã¢ãæãæµ®ãã¹ãã®ã¯ããªãã®å€¢ã§ãããã
ããããç§ãã¡ã¯ç°¡åãªæ¹æ³ã§é²ã¿ãŸã-ç§ãã¡ã¯ãã§ã«ã¢ã€ãã¢ãæã£ãŠãããšä»®å®ããç§ãã¡ã¯é·ãéããã«ã€ããŠèããŠããŸããã§ããã æåã®å£®å€§ãªãããžã§ã¯ããšããŠãObsidian-
Pathfinder Adventuresã®åªããã²ãŒã ã®ã¯ããŒã³ãäœæããŸãã
ãããã¯äžäœäœã ïŒ ããŒãã«ã¯ãããŸããïŒã圌ããèšãããã«ã
ããŒã«ã³ã€ãã¹ïŒ ç§ãã¡ã¯ãã§ã«åèŠãæŸæ£ããŠããããã«èŠããã®ã§ãç§ãã¡ã¯å€§èã«ã¢ã€ãã¢ãæŽç·Žãå§ããŸãã åœç¶ãã²ãŒã ã1察1ã§ã¯ããŒã³ããããšã¯ãããŸããããåºæ¬çãªã¡ã«ããºã ã¯åçšããŸãã ããã«ãã¿ãŒã³ããŒã¹ã®å調åããŒãã²ãŒã ã®å®è£
ã«ã¯æ¬¡ã®å©ç¹ããããŸãã
- ããã¯æ®µéçã§ã-ããã«ãããã¿ã€ããŒãåæãæé©åãFPSããã®ä»ã®éå±ãªããšãå¿é
ããå¿
èŠããªããªããŸãã
- ååçã§ããã€ãŸãããã¬ã€ã€ãŒå士ã¯ç«¶äºããŸããããæ±ºå®è«çãªã«ãŒã«ã«åŸã£ãŠç¹å®ã®ãç°å¢ããšå¯ŸæŠããŸããããã«ãããã²ãŒã éçºã®æãé£ããæ®µéã®1ã€ã§ããAI ïŒ AI ïŒãããã°ã©ã ããå¿
èŠããªããªããŸãã
- ããã¯æå³ããããŸã-ããŒãã«ãããã¯äžè¬ã«æ°ãŸãããªäººã
ã§ããã圌ãã¯äœãå·ã€ããŸããïŒåœŒãã«ææ
®æ·±ãã¡ã«ããºã ãšé¢çœãã²ãŒã ãã¬ã€ãäžããŸã-ããªãã¯1ã€ã®çŸããåçã«åºãããŸããïŒããã¯ããã€ãã®èº«è¿ãªãã®ã«äœããäžããŸããïŒïŒ
- å€ãã®eã¹ããŒããã³ã¯åæããŸããããå人çã«ã¯ã²ãŒã ã¯é¢çœãç©èªãèªãã¹ãã§ã-æ¬ã®ããã«ãç¹å¥ãªèžè¡çææ®µã䜿çšããã ãã§ãã
- 圌女ã¯é¢çœãã誰ããæ¥œãããããã§ã¯ãããŸããã説æããã¢ãããŒãã¯ãããªããäœäººããããšãããã®åŸã®å€¢ã«é©çšã§ããŸãã
ã«ãŒã«ã«æ
£ããŠããªã人ã®ããã«ãç°¡åãªç޹ä»ïŒPathfinder Adventuresã¯ãããŒãããŒã«ãã¬ã€ã³ã°ã²ãŒã ïŒãŸãã¯ããŒã«ãã¬ã€ã³ã°ã·ã¹ãã å
šäœïŒPathfinderã«åºã¥ããŠäœæãããããŒãã«ãŒãã²ãŒã ã®ããžã¿ã«ããŒãžã§ã³ã§ãã ãã¬ã€ã€ãŒïŒ1ã6人ïŒã¯èªåã§ãã£ã©ã¯ã¿ãŒãéžæãã圌ãšäžç·ã«ããã€ãã®ã·ããªãªã«åãããŠåéºã«åºãŸãã åãã£ã©ã¯ã¿ãŒã¯ãããŸããŸãªã¿ã€ãã®ã«ãŒãïŒæŠåšãé§ãåªæã峿¹ãã¢ã€ãã ãªã©ïŒãèªç±ã«äœ¿çšã§ããŸããåã·ããªãªã§ã圌ã¯ãã®å©ããåããŠãæªå
-ç¹å¥ãªç¹æ§ãæã€ç¹å¥ãªã«ãŒããèŠã€ããŠé
·ã眰ããªããã°ãªããŸããã
åã·ããªãªã¯ããã¬ã€ã€ãŒã蚪åããŠæ¢çŽ¢ããå¿
èŠãããå ŽæãŸãã¯å Žæã®æ°ïŒãããã®æ°ã¯ãã¬ã€ã€ãŒã®æ°ã«ãã£ãŠç°ãªããŸãïŒãæäŸããŸãã åå Žæã«ã¯è£åãã«ãªã£ãŠããã«ãŒãã®ããããå«ãŸããŠããããã£ã©ã¯ã¿ãŒã¯é çªã«æ¢çŽ¢ããŸããã€ãŸãããããã«ãŒããéããé¢é£ããã«ãŒã«ã«åŸã£ãŠãããå
æããããšããŸãã ãã¬ã€ã€ãŒã®ãããã«è£å
ããç¡å®³ãªã«ãŒãã«å ããŠããããã®ãããã«ã¯éªæªãªæµãé害ç©ãå«ãŸããŠããŸããããã«åé²ããã«ã¯ãããããåãå¿
èŠããããŸãã Scoundrelã«ãŒãããããã®1ã€ã«ãããŸããããã¬ã€ã€ãŒã¯ã©ã®ã«ãŒãããç¥ããŸãã-ãããèŠã€ããå¿
èŠããããŸãã
ã«ãŒããæã¡è² ããïŒããã³æ°ããã«ãŒããç²åŸããïŒããã«ããã£ã©ã¯ã¿ãŒã¯ããµã€ãºã察å¿ããç¹æ§ã®å€ïŒd4ããd12ïŒã«ãã£ãŠæ±ºå®ããããã€ã¹ãæããŠãç¹æ§ïŒRPGã®åŒ·ããåšçšããç¥æµãªã©ã®æšæºïŒã®ãã¹ãã«åæ Œããå¿
èŠããããŸãã«ãŒã«ãšãã£ã©ã¯ã¿ãŒéçºã®ã¬ãã«ïŒããããŠæããã®é©åãªã«ãŒãã®å¹æãé«ããããã«ãã¬ã€ããŸãã åå©ãããšãåºäŒã£ãã«ãŒãã¯ã²ãŒã ããåãé€ããããïŒæµã®å ŽåïŒããã¬ã€ã€ãŒã®ææãè£å
ãïŒã¢ã€ãã ã®å ŽåïŒãç§»åã¯å¥ã®ãã¬ã€ã€ãŒã«ç§»åããŸãã è² ãããšããã£ã©ã¯ã¿ãŒã¯ãã°ãã°ãã¡ãŒãžãåããæããã«ãŒããæšãŠãããŸãã è峿·±ãã¡ã«ããºã ã¯ããããã®ã«ãŒãã®æ°ã«ãã£ãŠãã£ã©ã¯ã¿ãŒã®äœåãæ±ºãŸããšããããšã§ãããã¬ã€ã€ãŒããããããã«ãŒããåŒãå¿
èŠããããšããã«ããã£ã©ã¯ã¿ãŒãããã«ããªããã°ããã£ã©ã¯ã¿ãŒã¯æ»ã«ãŸãã
ç®æšã¯ããã±ãŒã·ã§ã³ããããéãæããŠãScoundrelãèŠã€ããŠåããéåŽãžã®éã以åãããã¯ããŠããããšã§ãïŒããã«ã€ããŠã¯ãã«ãŒã«ãèªãããšã§ããã«åŠã¶ããšãã§ããŸãïŒã ãã ããããããã°ããè¡ãå¿
èŠãããããããã²ãŒã ã®äž»ãªé£ç¹ã§ãã æã®æ°ã¯å³å¯ã«å¶éãããŠãããå©çšå¯èœãªãã¹ãŠã®ã«ãŒãã®åçŽãªåæã§ã¯ç®æšã«éããŸããã ããŸããŸãªããªãã¯ãšã¹ããŒããªãã¯ããã¯ãé©çšããå¿
èŠãããããã§ãã
ã·ããªãªãæºãããããšããã£ã©ã¯ã¿ãŒã¯æé·ããŠçºéãããã£ã©ã¯ã¿ãŒã®ç¹æ§ãåäžããæ°ããæçšãªã¹ãã«ãç²åŸããŸãã ãããã®ç®¡çãã²ãŒã ã®éåžžã«éèŠãªèŠçŽ ã§ããã·ããªãªã®çµæïŒç¹ã«åŸã®æ®µéïŒã¯ãéåžžãé©åãªã«ãŒãã«äŸåããŸãïŒãããŠã幞éã«å€§ããäŸåããŸããããµã€ã³ãã䜿ã£ãã²ãŒã ã«ã¯äœãå¿
èŠã§ããïŒïŒã
äžè¬çã«ãã²ãŒã ã¯è峿·±ãã䟡å€ããããæ³šç®ã«å€ããŸãããããŠãç§ãã¡ã«ãšã£ãŠéèŠãªããšã¯ãã¯ããŒã³ãå®è£
ããããšãé¢çœãããããã«éåžžã«è€éã§ãïŒãé£ããããšã¯ãé£ããããšããæå³ã§ã¯ãããŸããïŒã
ãã®ã±ãŒã¹ã§ã¯ã1ã€ã®ã°ããŒãã«ãªæŠå¿µå€æŽãè¡ããŸã-ã«ãŒããæŸæ£ããŸãã ããããç§ãã¡ã¯ãŸã£ããæåŠããŸãããããµã€ãºãšè²ãç°ãªãç«æ¹äœã«ã«ãŒãã眮ãæããŸãïŒæè¡çã«ã¯ãæ£ããå
è§åœ¢ä»¥å€ã®åœ¢ç¶ãããããããç«æ¹äœãã䜿çšããããšã¯ãŸã£ããæ£ãããããŸããããã骚ããšåŒã¶ã®ã¯çããã§ãïŒããã¯äžå¿«ã§ãããã¢ã¡ãªã«ã®ãã€ãžãŒã䜿çšããããšã¯å³ã®ãããã®å
åãªã®ã§ããã®ãŸãŸã«ããŠãããŸãããïŒã ä»ããããã®ä»£ããã«ããã¬ã€ã€ãŒã¯ããã°ãæã£ãŠããŸãã ãŸããå Žæã«ã¯ããã°ããããããããç ç©¶ããã»ã¹ã®ãã¬ã€ã€ãŒãä»»æã®ãã¥ãŒããåŒãåºããŸãã ãã¥ãŒãã®è²ã«ãã£ãŠããã¥ãŒãã®ã¿ã€ããæ±ºãŸããããã«å¿ããŠããã¹ãã«åæ Œããããã®ã«ãŒã«ã決ãŸããŸãã ãã®çµæããã£ã©ã¯ã¿ãŒã®å人çãªç¹æ§ïŒåŒ·ããåšçšããªã©ïŒã¯ãªããªããŸãããæ°ããè峿·±ãã¡ã«ããºã ã衚瀺ãããŸãïŒè©³çްã¯åŸã»ã©ïŒã
éã¶ã®ã¯æ¥œããã§ããïŒ ç§ã«ã¯èŠåœãã€ãããå®éã®ãããã¿ã€ãã宿ãããŸã§èª°ããããçè§£ã§ããŸããã ããããã²ãŒã ãæ¥œããã®ã§ã¯ãªããéçºã楜ãã¿ãŸããïŒ ãããã£ãŠãæåã®çãã¯ãªãã¯ãã§ãã
ã¹ããã2 èšèš
ã¢ã€ãã¢ãæã€ããšã¯ãç©èªã®3åã®1ã«ãããŸããã ä»ããã®èããçºå±ãããããšãéèŠã§ãã ã€ãŸããå
¬åãæ£æ©ããããã¹ããŒã ãã¹ã济ã³ããããã®ã§ã¯ãªããããŒãã«ã«åº§ã£ãŠãã³ã§çŽãåãïŒãŸãã¯ãæ°ã«å
¥ãã®ããã¹ããšãã£ã¿ãŒãéãïŒããã¶ã€ã³ããã¥ã¡ã³ããæ
éã«äœæããã²ãŒã ã¡ã«ãã¯ã¹ã®ããããåŽé¢ãå
¥å¿µã«äœæããŸãã ããã«ã¯æéãããããŸãã®ã§ãäžæ°ã«æç« ã宿ãããããšãæåŸ
ããªãã§ãã ããã ãããŠããã¹ãŠãäžåºŠã«ãã¹ãŠæ€èšããããšããæãã§ããŸãã-å®è£
ããã«ã€ããŠãå€ãã®å€æŽãšå€æŽãè¡ãå¿
èŠãããããšãããããŸãïŒãããŠãæã«ã¯ã°ããŒãã«ã«äœããããçŽããŸãïŒããéçºããã»ã¹ãå§ãŸãåã«äœããã®åºç€ãååšããªããã°ãªããŸããã
æåã¯ãèšèšããã¥ã¡ã³ãã¯æ¬¡ã®ããã«ãªããŸã ãããŠã壮倧ãªã¢ã€ãã¢ã®æåã®æ³¢ã«å¯ŸåŠããåŸã«åããŠãé ãåããããã¥ã¡ã³ãã®æ§é ãæ±ºå®ãããããã³ã³ãã³ãã§æŽç¶ãšæºããå§ããŸãïŒäžèŠãªç¹°ãè¿ããç¹ã«ççŸãé¿ããããã«ããã§ã«æžãããå
å®¹ã§æ¯ç§ããã§ãã¯ããŸãïŒã 次第ã«ã次ã®ããã«æå³ã®ããç°¡æœãªãã®ãåŸãããŸãã
èšèšã説æãããšãã¯ãç¹ã«äžäººã§äœæ¥ããå Žåã¯ãèãã衚çŸããããèšèªãéžæããŠãã ããã ãããžã§ã¯ãã«ãµãŒãããŒãã£ã®éçºè
ãåå ãããå¿
èŠãããå Žåã¯ãããªãã®é ã®äžã§èµ·ãã£ãŠãããã¹ãŠã®åµé çãªãã³ã»ã³ã¹ã圌ããçè§£ããŠããããšã確èªããŠãã ããã
ç¶è¡ããã«ã¯ãåŒçšãããããã¥ã¡ã³ããå°ãªããšãæãã«èªãããšã匷ããå§ãããŸããå°æ¥çã«ã¯ããã®è§£éã«ã€ããŠè©³ãã説æããããšãªããããã«æç€ºãããŠããçšèªãšæŠå¿µãåç
§ããããã§ãã
ãèè
ãå£ã«åãã£ãŠèªæ®ºããŠãã ããã æåãå€ãããŸãããã¹ããã3 ã¢ããªã³ã°
ã€ãŸãããã¹ãŠåããã¶ã€ã³ã§ããã詳现ãªãã®ã§ãã
å€ãã®äººãIDEãéããŠã³ãŒãã£ã³ã°ãå§ããããšæã£ãŠããŸãããããå°ãå¿è匷ãåŸ
ã£ãŠãã ããã ã¢ã€ãã¢ãé ãå§åãããšãããŒããŒãã«è§Šããã ãã§æã空ã®é«ãã®è·é¢ã«çªé²ããããã«æããŸã-ã¹ããŒãã§ã³ãŒããŒã沞隰ããåã«ãã¢ããªã±ãŒã·ã§ã³ã®äœæ¥ããŒãžã§ã³ã¯ãŽãç®±ã«è¡ãæºåãã§ããŠããŸã... åãããšãäœåºŠãæžãçŽããªãããã«ïŒç¹ã«ã3æéã®éçºåŸã«ã¬ã€ã¢ãŠããæ©èœãããæ°ãã«éå§ããå¿
èŠãããããšã確èªããªãããã«ïŒãæåã«ã¢ããªã±ãŒã·ã§ã³ã®äž»èŠæ§é ãæ€èšïŒããã³ææžåïŒããããšããå§ãããŸãã
éçºè
ãšããŠããªããžã§ã¯ãæåããã°ã©ãã³ã°ïŒOOPïŒã«ç²ŸéããŠããã®ã§ããããžã§ã¯ãã§ãã®ååã䜿çšããŸãã ããããOOPã®å Žåãéå±ãªUMLãã€ã¢ã°ã©ã ã®æããéçºãéå§ããããšã»ã©æåŸ
ãããŠããŸããã ïŒ
UMLãäœãªã®ãããããŸãããïŒç§ãå¿ãããã«ãªããŸããããåãã§æãåºããŸã-ç§ãå€åãªããã°ã©ããŒã§ããããšã瀺ãããã§ãã
ãŠãŒã¹ã±ãŒã¹å³ããå§ããŸãããã ãŠãŒã¶ãŒïŒãã¬ãŒã€ãŒïŒãå°æ¥ã®ã·ã¹ãã ãšããåãããæ¹æ³ã説æããŸãã
ããããš...ããã¯äœã®ããšã§ããïŒãåè«ã§ããåè«ã§ã...ãããŠããããããç§ã¯ããã«ã€ããŠåè«ãèšãã®ããããŸã-ããã¯æ·±å»ãªåé¡ã§ãïŒçµå±ã倢ã§ãïŒã ãŠãŒã¹ã±ãŒã¹ã®å³ã§ã¯ãã·ã¹ãã ããŠãŒã¶ãŒã«æäŸããå¯èœæ§ã衚瀺ããå¿
èŠããããŸãã 詳现ã«ã ãããããã®ç¹å®ã®ã¿ã€ãã®ãã€ã¢ã°ã©ã ãç§ã«ãšã£ãŠææªã§ããããšã倿ããã®ã¯æŽå²çã«èµ·ãããŸãã-æããã«ååãªå¿èããããŸããã ãããŠãããªãã¯ãã®ããã«ç§ãèŠãå¿
èŠã¯ãããŸãã-ç§ãã¡ã¯å€§åŠã§åæ¥èšŒæžãä¿è·ããŠããŸããããç§ãã¡ã¯ä»äºã®ããã»ã¹ã楜ããã§ããŸãã ãã®ããã»ã¹ã§ã¯ããŠãŒã¹ã±ãŒã¹ã¯ããã»ã©éèŠã§ã¯ãããŸããã ã¢ããªã±ãŒã·ã§ã³ãç¬ç«ããã¢ãžã¥ãŒã«ã«æ£ããåå²ããããšãã€ãŸããããžã¥ã¢ã«ã€ã³ã¿ãŒãã§ã€ã¹ã®æ©èœãã²ãŒã ã¡ã«ãã¯ã¹ã«åœ±é¿ãäžããªãããã«ã²ãŒã ãå®è£
ããå¿
èŠã«å¿ããŠã°ã©ãã£ãã¯ã³ã³ããŒãã³ããç°¡åã«å€æŽã§ããããã«ããããšãã¯ããã«éèŠã§ãã
ãã®ç¹ã¯ã次ã®ã³ã³ããŒãã³ãå³ã§è©³ãã説æã§ããŸãã
ã·ã¹ãã ã³ã³ããŒãã³ã ããã§ãã¢ããªã±ãŒã·ã§ã³ã®äžéšã§ããç¹å®ã®ãµãã·ã¹ãã ããã§ã«ç¹å®ããŸãããåŸã§ç€ºãããã«ããããã¯ãã¹ãŠäºãã«ç¬ç«ããŠéçºãããŸãã
ãŸããåãæ®µéã§ãã²ãŒã ã®ã¡ã€ã³ãµã€ã¯ã«ãã©ã®ããã«èŠããããæšå®ããŸãïŒãŸãã¯ãæãè峿·±ãéšåã¯ãã¹ã¯ãªããå
ã®ãã£ã©ã¯ã¿ãŒãå®è£
ããéšåã§ãïŒã ããã«ã¯ãã¢ã¯ãã£ããã£å³ãé©ããŠããŸãã
ãããŠæåŸã«ãå
¥åºåã·ã¹ãã ãä»ãããšã³ããŠãŒã¶ãŒãšã²ãŒã ãšã³ãžã³ãšã®çžäºäœçšã®ã·ãŒã±ã³ã¹ãäžè¬çãªçšèªã§ç€ºããšããã§ãããã
å€ã¯é·ãã倿ãã®ãã£ãšåã§ãã ããŒãã«ã«åº§ã£ãåŸãä»ã®20åã®å³ãèœã¡çããŠæããŸããå°æ¥çã«ã¯ãããããååšããããšã§ãéžæããéãæ©ã¿ãèªå°å¿ãé«ããéšå±ã®ã€ã³ããªã¢ãæŽæ°ããè²ãããå£çŽã§è²ãããå£çŽãåãããç°¡åã«ããªãã®ããžã§ã³ãäŒããããšãã§ããŸãããã«ããªãã®æ°ããã¹ã¿ãžãªã®ãã¢ã«å€§æ¥ãã§é§ãã€ãã仲éã®éçºè
ïŒç§ãã¡ã¯æåãç®æããŠããŸãããèŠããŠããŸããïŒïŒ
ãããŸã§ã®ãšããã誰ããæããã¯ã©ã¹å³ïŒã¯ã©ã¹ïŒãåŒçšããã€ããã¯ãããŸãã-å€ãã®ã¯ã©ã¹ã
çªç ŽããããšãæåŸ
ãããŠãããæåã¯3ã€ã®ç»é¢ã®ç»åã¯è¿œå ãããŸããã é©åãªãµãã·ã¹ãã ã®éçºã«é²ãã«ã€ããŠãæçã«åå²ããŠåŸã
ã«é
眮ããæ¹ãé©åã§ãã
ã¹ããã4 ããŒã«éžæ
ãã§ã«åæããããã«ãããŸããŸãªãªãã¬ãŒãã£ã³ã°ã·ã¹ãã ãå®è¡ãããã¹ã¯ããããšã¢ãã€ã«ããã€ã¹ã®äž¡æ¹ã§å®è¡ãããã¯ãã¹ãã©ãããã©ãŒã ã¢ããªã±ãŒã·ã§ã³ãéçºããŸããããã°ã©ãã³ã°èšèªãšããŠJavaãéžæããŸããåŸè
ã¯ããæ°ããæ°é®®ãªãããKotlinã¯ããã«åªããŠãããåä»»è
ãå§åããinãã®æ³¢ã§æ³³ãæéã¯ãŸã ãããŸããïŒåæã«ã誰ãããããææããŠããªãå Žåã¯ãã¬ãŒãã³ã°ããŸãïŒãJVMã¯ãåãã®ãšãããã©ãã§ãå©çšã§ããŸãïŒ30åå°ã®ããã€ã¹ã§ïŒãWindowsãšUNIXã®äž¡æ¹ããµããŒãããSSHæ¥ç¶ãä»ãããªã¢ãŒããµãŒããŒäžã§ãåçã§ããŸãïŒå¿
èŠãªå Žåã¯äžæã§ããããã®ãããªæ©äŒãæäŸããŸãïŒããŸããéæã¡ã«ãªã£ãŠã¢ãŒãã£ã¹ããéããšãã«Androidã«è»¢éããŸãããããã«ã€ããŠã¯åŸã§è©³ãã説æããŸããã©ã€ãã©ãªïŒã©ã€ãã©ãªãªãã§ã¯ã©ãã«ãã¢ã¯ã»ã¹ã§ããŸããïŒã¯ãã¹ãã©ãããã©ãŒã ã®èŠä»¶ã«å¿ããŠéžæããŸããMavenããã«ãã·ã¹ãã ãšããŠäœ¿çšããŸãããŸãã¯GradleããããšããMavenãããããå§ããŸããããããã«ãããŒãžã§ã³ç®¡çã·ã¹ãã ïŒå¥œããªãã®ïŒãã»ããã¢ããããããšããå§ãããŸããããããããšã§ãäœå¹Žãçµã£ãåŸãããããã€ãŠã©ãã»ã©çŽ æŽãããã£ãããæãããæ°æã¡ã§æãåºãããããªããŸããIDEã¯ã䜿ãæ
£ããããæ°ã«å
¥ãã®ã䟿å©ãªãã®ãéžæããŸããå®éãä»ã«äœãå¿
èŠãããŸãããéçºãéå§ã§ããŸããã¹ããã5 ãããžã§ã¯ãã®äœæãšèšå®
IDEã䜿çšããå Žåããããžã§ã¯ãã®äœæã¯ç°¡åã§ãã ç§ãã¡ã®å°æ¥ã®åäœã®ããã«ããã€ãã®éªæªãªååïŒããšãã°ã
Dice ïŒãéžæããå¿
èŠããããèšå®ã§MavenãµããŒããæå¹ã«ããããšãå¿ããã«ã
pom.xml
ãã¡ã€ã«ã«å¿
èŠãªèå¥åãæžã蟌ã¿ãŸãã
<modelVersion>4.0.0</modelVersion> <groupId>my.company</groupId> <artifactId>dice</artifactId> <version>1.0</version> <packaging>jar</packaging>
ãŸããããã©ã«ãã§ã¯ååšããªãKotlinãµããŒãã远å ããŸãã
<dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>${kotlin.version}</version> </dependency>
ãããŠãç§ãã¡ã詳ãã説æããªãããã€ãã®èšå®ïŒ
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <kotlin.version>1.3.20</kotlin.version> <kotlin.compiler.incremental>true</kotlin.compiler.incremental> </properties>
ãã€ããªãããããžã§ã¯ãã«é¢ããå°ãã®æ
å ±ãããžã§ã¯ãã§JavaãšKotlinã®äž¡æ¹ã䜿çšããå Žåã¯ã
src/main/kotlin
ã«å ããŠã
src/main/java
ãã©ã«ããŒããããŸãã Kotlinéçºè
ã¯ãæåã®ãã©ã«ããŒïŒ
*.kt
ïŒã®ãœãŒã¹ãã¡ã€ã«ã¯2çªç®ã®ãã©ã«ããŒïŒ
*.java
ïŒã®ãœãŒã¹ãã¡ã€ã«ãããæ©ãã³ã³ãã€ã«ããå¿
èŠããããšäž»åŒµããŠãããããæšæºã®Mavenã¿ãŒã²ããã®èšå®ã倿Žããããšã匷ããå§ãããŸãã
<build> <plugins> <plugin> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-maven-plugin</artifactId> <version>${kotlin.version}</version> <executions> <execution> <id>compile</id> <phase>process-sources</phase> <goals> <goal>compile</goal> </goals> <configuration> <sourceDirs> <sourceDir>${project.basedir}/src/main/kotlin</sourceDir> <sourceDir>${project.basedir}/src/main/java</sourceDir> </sourceDirs> </configuration> </execution> <execution> <id>test-compile</id> <goals> <goal>test-compile</goal> </goals> <configuration> <sourceDirs> <sourceDir>${project.basedir}/src/test/kotlin</sourceDir> <sourceDir>${project.basedir}/src/test/java</sourceDir> </sourceDirs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <executions> <execution> <id>default-compile</id> <phase>none</phase> </execution> <execution> <id>default-testCompile</id> <phase>none</phase> </execution> <execution> <id>java-compile</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>java-test-compile</id> <phase>test-compile</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
ãããã©ãã»ã©éèŠãã¯èšããŸããããã®ã·ãŒãããªããã°ãããžã§ã¯ãã¯é 調ã«é²ãã§ããŸãã ãã ãã念ã®ãããèŠåã衚瀺ãããŸãã
äžåºŠã«3ã€ã®ããã±ãŒãžãäœæããŠã¿ãŸãããïŒãªãäœã詊ããŠã¿ãŸãããïŒïŒïŒ
model
-ã²ãŒã ã¯ãŒã«ãã®ãªããžã§ã¯ããèšè¿°ããã¯ã©ã¹ã- game-ã²ãŒã ãã¬ã€ãå®è£
ããã¯ã©ã¹çšã
ui
ãŠãŒã¶ãŒå¯Ÿè©±ãæ
åœããã¯ã©ã¹çšã
åŸè
ã«ã¯ã€ã³ã¿ãŒãã§ãŒã¹ã®ã¿ãå«ãŸãããã®ã¡ãœããã¯ããŒã¿ã®å
¥åºåã«äœ¿çšããŸãã äžè¬ã«ç¹å®ã®å®è£
ãå¥ã®ãããžã§ã¯ãã«ä¿åããŸãããããã«ã€ããŠã¯åŸã§è©³ãã説æããŸãã ãããŸã§ã¯ãã¹ãã¬ãŒããããªãããã«ããããã®ã¯ã©ã¹ã䞊ã¹ãŠè¿œå ããŸãã
ããã«å®ç§ã«ããããšããªãã§ãã ãããããã±ãŒãžåãã€ã³ã¿ãŒãã§ãŒã¹ãã¯ã©ã¹ãã¡ãœããã®è©³çްãçèããŠãã ããã ãªããžã§ã¯ãå士ã®çžäºäœçšã培åºçã«èŠå®ããŸã-ããã¯ãã¹ãŠãæ°åå以äžå€åããŸãã ãããžã§ã¯ããçºå±ããã«ã€ããŠãå€ãã®ãã®ãugããŠããã°ããããªãã«ãšã£ãŠã广çã§ãªãããã«èŠããŸã-çŸä»£ã®IDEã§ã®ãªãã¡ã¯ã¿ãªã³ã°ã¯éåžžã«å®äŸ¡ãªæäœãªã®ã§ãæ°è»œã«å€æŽããŠãã ããã
ãŸãã
main
æ©èœãåããã¯ã©ã¹ãäœæããŸããããã§ããã°ãããææã«åããããšãã§ããŸãã IDEèªäœãèµ·åã«äœ¿çšã§ããŸãããåŸã§èŠãããã«ããã®æ¹æ³ã¯ç§ãã¡ã®ç®çã«ã¯é©ããŠããŸããïŒæšæºã®IDEã³ã³ãœãŒã«ã¯ãå¿
èŠã«å¿ããŠã°ã©ãã£ã«ã«ãªçµæã衚瀺ã§ããŸããïŒããããã£ãŠããããïŒãŸãã¯UNIXã·ã¹ãã ã®ã·ã§ã«ïŒã䜿çšããŠå€éšããã®èµ·åãæ§æããŸããã¡ã€ã«ã ãã ãããã®åã«ãããã€ãã®è¿œå èšå®ãè¡ããŸãã
mvn package
æäœãå®äºããåŸãã³ã³ãã€ã«ããããã¹ãŠã®ã¯ã©ã¹ãå«ãJARã¢ãŒã«ã€ãã®åºåãååŸããŸãã ãŸããããã©ã«ãã§ã¯ããã®ã¢ãŒã«ã€ãã«ã¯ããããžã§ã¯ããæ©èœããããã«å¿
èŠãªäŸåé¢ä¿ãå«ãŸããŠããŸããïŒãããŸã§ã®ãšããããããã¯ãããŸããããå°æ¥çã«ã¯ç¢ºå®ã«è¡šç€ºãããŸãïŒã 次ã«ã
main
ã¡ãœãããå«ãã¡ã€ã³ã¯ã©ã¹ãžã®ãã¹ãã¢ãŒã«ã€ããããã§ã¹ããã¡ã€ã«ã§æå®ãããŠããªãããã
java -jar dice-1.0.jar
ãããžã§ã¯ããéå§
java -jar dice-1.0.jar
ãŸããã ãããä¿®æ£ããã«ã¯ã
pom.xml
远å ã®èšå®ã远å ããŸãã
<build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>my.company.dice.MainKt</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build>
ã¡ã€ã³ã¯ã©ã¹ã®ååã«æ³šæããŠãã ããã ã¯ã©ã¹ã®å€éšã«å«ãŸããKotlin颿°ïŒ
main
颿°ãªã©ïŒã®å Žåãã¯ã©ã¹ã¯ã³ã³ãã€ã«äžã«äœæãããŸãïŒJVMã¯äœãç¥ãããç¥ããããªãããïŒã ã¯ã©ã¹ã®ååã¯ã
Kt
远å ãããã¡ã€ã«ã®ååã§ãã ã€ãŸããã¡ã€ã³ã¯ã©ã¹ã«
Main
ãšããååãä»ããå Žåã
MainKt.class
ãã¡ã€ã«ã«ã³ã³ãã€ã«ãããŸãã jarãã¡ã€ã«ã®ãããã§ã¹ãã§ç€ºãå¿
èŠãããã®ã¯ããã®æåŸã®1ã€ã§ãã
ããã§ããããžã§ã¯ãããã«ããããšãåºåã«2ã€ã®jarãã¡ã€ã«
dice-1.0.jar
ããã³
dice-1.0-jar-with-dependencies.jar
ãååŸãããŸãã 第äºã«èå³ããããŸãã ã¹ã¿ãŒãã¢ããã¹ã¯ãªãããäœæããŸãã
dice.bat ïŒWindowsçšïŒ
@ECHO OFF rem Compiling call "path_to_maven\mvn.bat" -f "path_to_project\Dice\pom.xml" package if errorlevel 1 echo Project compilation failed! & pause & goto :EOF rem Running java -jar path_to_project\Dice\target\dice-1.0-jar-with-dependencies.jar pause
dice.sh ïŒUNIXã®å ŽåïŒ
ã³ã³ãã€ã«ã倱æãããšãã¹ã¯ãªããã®äžæã匷å¶ãããããšã«æ³šæããŠãã ããã ãã以å€ã®å ŽåãæåŸã®ããŒãã§ã¯ãªããååã®æ£åžžãªã¢ã»ã³ããªããæ®ã£ãŠãããã¡ã€ã«ãèµ·åãããŸãïŒéããèŠã€ãããªãããšããããŸãïŒã å€ãã®å Žåãéçºè
ã¯
mvn clean package
ã³ãã³ãã䜿çšããŠä»¥åã«ã³ã³ãã€ã«ããããã¹ãŠã®ãã¡ã€ã«ãåé€ããŸããããã®å Žåãã³ã³ãã€ã«ããã»ã¹å
šäœã¯åžžã«ïŒãœãŒã¹ã³ãŒãã倿ŽãããŠããªããŠãïŒæåããéå§ãããå€ãã®æéãããããŸãã ããããåŸ
ã€ããšã¯ã§ããŸãã-ã²ãŒã ãäœæããå¿
èŠããããŸãã
ãã®ããããããžã§ã¯ãã¯æ£åžžã«èµ·åããŸããããããŸã§ã®ãšããäœãããŸããã å¿é
ããªãã§ãã ãããããã«ä¿®æ£ããŸãã
ã¹ããã6 äž»ãªãªããžã§ã¯ã
åŸã
ã«ãã²ãŒã ãã¬ã€ã«å¿
èŠãªã¯ã©ã¹ã
model
ããã±ãŒãžã«è¿œå ãå§ããŸãã
ãã¥ãŒãããã¹ãŠã§ããæåã«è¿œå ããŸãã åãã€ïŒ
Die
ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ïŒã¯ããã®ã¿ã€ãïŒè²ïŒãšãµã€ãºã«ãã£ãŠç¹åŸŽä»ããããŸãã ãã¥ãŒãã®ã¿ã€ãã«ã€ããŠã¯ãåå¥ã®åæïŒ
Die.Type
ïŒãäœæãããµã€ãºã4ãã12ã®æŽæ°ã§ããŒã¯ããŸãããŸãã
roll()
ã¡ãœãããå®è£
ã
roll()
ãããã¯ããã¥ãŒãã§äœ¿çšå¯èœãªç¯å²ïŒ1ãããµã€ãºå€ãŸã§ïŒããä»»æã®åäžã«åæ£ããæ°ãçæããŸãã
ã¯ã©ã¹ã¯ããã¥ãŒããçžäºã«æ¯èŒã§ãã
Comparable
ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããŸãïŒåŸã§è€æ°ã®ãã¥ãŒããé åºä»ããããè¡ã«è¡šç€ºãããšãã«äŸ¿å©ã§ãïŒã ãã倧ããªãã¥ãŒãã¯ããæ©ãé
眮ãããŸãã
class Die(val type: Type, val size: Int) : Comparable<Die> { enum class Type { PHYSICAL,
ã»ãããéããªãããã«ããã¥ãŒãã¯ãã³ãããã°ïŒ
Bag
ã¯ã©ã¹ã®ã³ããŒïŒã«ä¿ç®¡ãããŸãã ããã°å
ã§äœãèµ·ãã£ãŠããããæšæž¬ããããšããã§ããªããããé åºä»ããããã³ã¬ã¯ã·ã§ã³ã䜿çšããæå³ã¯ãããŸããã ã®ããã§ãã ã»ããïŒã»ããïŒã¯å¿
èŠãªã¢ã€ãã¢ãããŸãââå®è£
ããŸããã2ã€ã®çç±ã§é©åããŸããã ãŸãããããã䜿çšãããšãã¯ã
equals()
ããã³
hashCode()
ã¡ãœãããå®è£
ããå¿
èŠããããŸããããã¥ãŒãã®ã¿ã€ããšãµã€ãºãæ¯èŒããã®ã¯ééã£ãŠãããããã©ã®ããã«ããã°ãããæç¢ºã§ã¯ãããŸãã-ä»»æã®æ°ã®åããã¥ãŒããã»ããã«æ ŒçŽã§ããŸãã 第äºã«ããã¥ãŒããè¢ããåãåºããšãæ¯åç°ãªãéæ±ºå®çãªãã®ã ãã§ãªããã©ã³ãã ãªãã®ãåŸãããããšãæåŸ
ãããŸãã ãããã£ãŠãé åºä»ããããã³ã¬ã¯ã·ã§ã³ïŒãªã¹ãïŒã䜿çšããæ°ããèŠçŽ ã远å ãããã³
put()
ã¡ãœããã§ïŒãŸãã¯çºè¡ããçŽåïŒ
draw()
ã¡ãœããã§ïŒã·ã£ããã«ããããšããå§ãããŸãã
examine()
ã¡ãœããã¯ãäžç¢ºå®æ§ã«é£œã飜ãããŠãããã¬ãŒã€ãŒãããŒãã®ããŒãã«ã®ããã°ã®äžèº«ãæ¯ãå ŽåïŒãœãŒãã«æ³šæãæãïŒãšã
clear()
ã¡ãœãã-æ¯ããããã¥ãŒããããã°ã«æ»ããªãå Žåã«é©ããŠããŸãã
open class Bag { protected val dice = LinkedList<Die>() val size get() = dice.size fun put(vararg dice: Die) { dice.forEach(this.dice::addLast) this.dice.shuffle() } fun draw(): Die = dice.pollFirst() fun clear() = dice.clear() fun examine() = dice.sorted().toList() }
ãã¥ãŒãä»ãããã°ã«å ããŠããã¥ãŒãä»ãããŒãïŒ
Pile
ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ïŒãå¿
èŠã§ãã 1ã€ç®ãš2ã€ç®ã¯ãã³ã³ãã³ãããã¬ãŒã€ãŒã«è¡šç€ºãããç¹ã§ç°ãªããŸãããããã£ãŠãå¿
èŠã«å¿ããŠããŒããããã¥ãŒããåé€ãããã¬ãŒã€ãŒã¯ç¹å®ã®ã€ã³ã¹ã¿ã³ã¹ãéžæã§ããŸãã
removeDie()
ã¡ãœããã䜿çšããŠãã®ã¢ã€ãã¢ãå®è£
ããŸãã
class Pile : Bag() { fun removeDie(die: Die) = dice.remove(die) }
次ã«ã䞻人å
¬ã§ããããŒããŒã«ç®ãåããŸãã ã€ãŸããä»åŸãã£ã©ã¯ã¿ãŒãããŒããŒãšåŒã³ãŸãïŒJavaã§
Character
ãšããååã§ã¯ã©ã¹ãåŒã³åºããªãçç±ã¯ãããŸãïŒã ãã£ã©ã¯ã¿ãŒã«ã¯ããŸããŸãªçš®é¡ããããŸããïŒã¯ã©ã¹ã«å
¥ããã«ã¯åèª
class
ã䜿çšããªãã»ããè¯ãã§ãïŒãäœæ¥äžã®ãããã¿ã€ãã§ã¯ã
Brawler ïŒã€ãŸãã匷ããšåŒ·ãã«éç¹ã眮ããFighterïŒãš
Hunter ïŒå¥åRanger / Thiefãã«éç¹ã眮ããŸãïŒåšçšããšã¹ãã«ã¹ïŒã 䞻人å
¬ã®ã¯ã©ã¹ã¯åœŒã®ç¹æ§ãã¹ãã«ããã¥ãŒãã®åæã»ãããæ±ºå®ããŸãããåŸã§èŠãããããã«ã䞻人å
¬ã¯ã¯ã©ã¹ã«å³å¯ã«çµã³ä»ããããªããããå人èšå®ã¯1ãæã§ç°¡åã«å€æŽã§ããŸãã
ãã¶ã€ã³ããã¥ã¡ã³ãã«åŸã£ãŠãããŒããŒã«å¿
èŠãªããããã£ã远å ããŸããããååããæ°ã«å
¥ãã®ãã¥ãŒãã®çš®é¡ããµã€ã³ãã®å¶éãç¿åŸæžã¿ã®ã¹ãã«ãæªçãªã¹ãã«ããªã»ããçšã®æãããã°ããã€ã«ã ã³ã¬ã¯ã·ã§ã³ããããã£ã®å®è£
ã®æ©èœã«æ³šæããŠãã ããã ææäžçå
šäœã§ããªããžã§ã¯ãå
ã«æ ŒçŽãããŠããã³ã¬ã¯ã·ã§ã³ãžã®å€éšã¢ã¯ã»ã¹ïŒã²ãã¿ãŒã®å©ããåããŠïŒãæäŸããã®ã¯æªã圢ãšèããããŠããŸã-æªåŸ³ããã°ã©ããŒã¯ãã¯ã©ã¹ã®ç¥èããªããŠããããã®ã³ã¬ã¯ã·ã§ã³ã®å
容ã倿Žã§ããŸãã ããã«å¯ŸåŠãã1ã€ã®æ¹æ³ã¯ãèŠçŽ ã远å ããã³åé€ãããã®çªå·ãååŸããã€ã³ããã¯ã¹ã§ã¢ã¯ã»ã¹ããããã®åå¥ã®ã¡ãœãããå®è£
ããããšã§ãã ã²ãã¿ãŒãå®è£
ã§ããŸãããåæã«ã³ã¬ã¯ã·ã§ã³èªäœã§ã¯ãªãããã®äžå€ã®ã³ããŒãè¿ããŸã-å°æ°ã®èŠçŽ ã®å Žåãããã ããå®è¡ããããšã¯ç¹ã«æããããŸããã
data class Hero(val type: Type) { enum class Type { BRAWLER HUNTER } var name = "" var isAlive = true var favoredDieType: Die.Type = Die.Type.ALLY val hand = Hand(0) val bag: Bag = Bag() val discardPile: Pile = Pile() private val diceLimits = mutableListOf<DiceLimit>() private val skills = mutableListOf<Skill>() private val dormantSkills = mutableListOf<Skill>() fun addDiceLimit(limit: DiceLimit) = diceLimits.add(limit) fun getDiceLimits(): List<DiceLimit> = Collections.unmodifiableList(diceLimits) fun addSkill(skill: Skill) = skills.add(skill) fun getSkills(): List<Skill> = Collections.unmodifiableList(skills) fun addDormantSkill(skill: Skill) = dormantSkills.add(skill) fun getDormantSkills(): List<Skill> = Collections.unmodifiableList(dormantSkills) fun increaseDiceLimit(type: Die.Type) { diceLimits.find { it.type == type }?.let { when { it.current < it.maximal -> it.current++ else -> throw IllegalArgumentException("Already at maximum") } } ?: throw IllegalArgumentException("Incorrect type specified") } fun hideDieFromHand(die: Die) { bag.put(die) hand.removeDie(die) } fun discardDieFromHand(die: Die) { discardPile.put(die) hand.removeDie(die) } fun hasSkill(type: Skill.Type) = skills.any { it.type == type } fun improveSkill(type: Skill.Type) { dormantSkills .find { it.type == type } ?.let { skills.add(it) dormantSkills.remove(it) } skills .find { it.type == type } ?.let { when { it.level < it.maxLevel -> it.level += 1 else -> throw IllegalStateException("Skill already maxed out") } } ?: throw IllegalArgumentException("Skill not found") } }
ããŒããŒã®æïŒåœŒãçŸåšæã£ãŠããç«æ¹äœïŒã¯ãå¥ã®ãªããžã§ã¯ãïŒ
Hand
ã¯ã©ã¹ïŒã«ãã£ãŠèšè¿°ãããŸãã é£åãã¥ãŒããã¡ã€ã³ã¢ãŒã ããåé¢ããŠãããšããèšèšäžã®æ±ºå®ã¯ãæåã«æãã€ãããã®ã®1ã€ã§ããã æåã¯éåžžã«ã¯ãŒã«ãªæ©èœã®ããã«èŠããŸããããåŸã«èšå€§ãªæ°ã®åé¡ãšäžäŸ¿ãçã¿åºããŸããã ããã§ããç°¡åãªæ¹æ³ãæ¢ããŠããããã§ã¯ãªãããã
dice
ãš
allies
ãªã¹ãã¯ãµãŒãã¹ã«ããã远å ãåä¿¡ãåé€ããå¿
èŠããããã¹ãŠã®ã¡ãœããããããŸãïŒãã®ãã¡ã®ããã€ãã¯ã2ã€ã®ãªã¹ãã®ã©ã¡ãã«ã¢ã¯ã»ã¹ããããå·§åŠã«æ±ºå®ããŸãïŒã æãããã¥ãŒããåé€ãããšãåŸç¶ã®ãã¹ãŠã®ãã¥ãŒãããªã¹ãã®æäžéšã«ç§»åãã空çœãåããŸã-å°æ¥çã«ã¯æ€çŽ¢ã倧å¹
ã«å®¹æã«ãªããŸãïŒ
null
ç¶æ³ãåŠçããå¿
èŠã¯ãããŸããïŒã
class Hand(var capacity: Int) { private val dice = LinkedList<Die>() private val allies = LinkedList<Die>() val dieCount get() = dice.size val allyDieCount get() = allies.size fun dieAt(index: Int) = when { (index in 0 until dieCount) -> dice[index] else -> null } fun allyDieAt(index: Int) = when { (index in 0 until allyDieCount) -> allies[index] else -> null } fun addDie(die: Die) = when { die.type == Die.Type.ALLY -> allies.addLast(die) else -> dice.addLast(die) } fun removeDie(die: Die) = when { die.type == Die.Type.ALLY -> allies.remove(die) else -> dice.remove(die) } fun findDieOfType(type: Die.Type): Die? = when (type) { Die.Type.ALLY -> if (allies.isNotEmpty()) allies.first else null else -> dice.firstOrNull { it.type == type } } fun examine(): List<Die> = (dice + allies).sorted() }
DiceLimit
ã¯ã©ã¹ã®ãªããžã§ã¯ãã®ã³ã¬ã¯ã·ã§ã³ã¯ãããŒããŒãã¹ã¯ãªããã®æåã«æã€ããšãã§ããåã¿ã€ãã®ãã¥ãŒãã®æ°ã«å¶éãèšå®ããŸãã ç¹å¥ãªããšã¯ãããŸãããæåã«ãåã¿ã€ãã®æå€§å€ãšçŸåšã®å€ã決å®ããŸãã
class DiceLimit(val type: Die.Type, val initial: Int, val maximal: Int, var current: Int)
ããããã¹ãã«ãããã°ãã£ãšé¢çœãã§ãã ãããããåå¥ã«å®è£
ããå¿
èŠããããŸãïŒè©³çްã¯åŸã»ã©èª¬æããŸãïŒãã
Hitãš
Shoot ïŒã¯ã©ã¹ããšã«1ã€ïŒã®2ã€ã ããæ€èšããŸãã ã¹ãã«ã¯ãåæã¬ãã«ããæå€§ã¬ãã«ãŸã§éçºïŒããã³ããïŒã§ããŸããããã¯ããµã€ã³ãã®ããŒã«ã«è¿œå ãããä¿®æ£ã«åœ±é¿ãäžããããšããããããŸãã ããã¯ãããããã£
level
ã
maxLevel
ã
modifier1
ããã³
modifier2
maxLevel
ããŸãã
class Skill(val type: Type) { enum class Type {
Hero
ã¯ã©ã¹ã®è£å©ã¡ãœããã«æ³šæããŠãã ãããããã«ãããæãããã€ã¹ãé ãããããŒã«ããããããŒããŒã«ç¹å®ã®ã¹ãã«ããããã©ããã確èªããããåŠç¿ããã¹ãã«ã®ã¬ãã«ãäžããããæ°ããã¹ãã«ãç¿åŸãããã§ããŸãã ãããã¯ãã¹ãŠé
ããæ©ããå¿
èŠã«ãªããŸãããä»ã¯ãããã«ã€ããŠè©³ããã¯èª¬æããŸããã
äœæããªããã°ãªããªãã¯ã©ã¹ã®æ°ãæããªãã§ãã ããã ãã®è€éãªãããžã§ã¯ãã§ã¯ãæ°çŸãäžè¬çãªãã®ã§ãã ããã§ã¯ãããããæ·±å»ãªè·æ¥ãšåæ§ã«ãç§ãã¡ã¯å°ããªããšããå§ããåŸã
ã«ããŒã¹ãäžããŠãããŸãã1ãæã§ãç§ãã¡ã¯ãã®ç¯å²ã«æããããªããŸãã å¿ããªãã§ãã ãããç§ãã¡ã¯ãŸã äžäººã®å°ããªã¹ã¿ãžãªã§ã-ç§ãã¡ã¯å§åçãªä»äºã«çŽé¢ããŠããŸããã
ãäœããããããããã ç§ã¯ã¿ãã³ãåžãããäœã...ããããŠãç§ãã¡ã¯ç¶ããŸãã
䞻人å
¬ãšãã®èœåã説æãããŠããŸãããä»åºŠã¯æµã®å-倧ãããŠæãããã²ãŒã ã¡ã«ãã¯ã¹ã«è¡ããŸãããã ããããããŒããŒã察話ããªããã°ãªããªããªããžã§ã¯ãã
ç§ãã¡ã®åæ¢ãªäž»äººå
¬ã¯ã3çš®é¡ã®ãã¥ãŒããšã«ãŒãã«çŽé¢ããŸãïŒæªåœ¹ïŒ
Villain
ã¯ã©ã¹ïŒãæµïŒ
Enemy
ã¯ã©ã¹ïŒãé害ïŒ
Obstacle
ã¯ã©ã¹ïŒããè
åšããšããäžè¬çšèªã®äžã§å£çµããŸãéå®ïŒã åè
åšã«ã¯ããã®ãããªè
åšã«çŽé¢ãããšãã®è¡åã®ç¹å¥ãªã«ãŒã«ãèšè¿°ããã²ãŒã ãã¬ã€ã«å€æ§æ§ã远å ããäžé£ã®ç¹åŸŽçãªæ©èœïŒ
Trait
ïŒããããŸãã
sealed class Threat { var name: String = "" var description: String = "" private val traits = mutableListOf<Trait>() fun addTrait(trait: Trait) = traits.add(trait) fun getTraits(): List<Trait> = traits } class Obstacle(val tier: Int, vararg val dieTypes: Die.Type) : Threat() class Villain : Threat() class Enemy : Threat() enum class Trait { MODIFIER_PLUS_ONE,
Trait
ã¯ã©ã¹ã®ãªããžã§ã¯ãã®ãªã¹ãã¯å¯å€ïŒ
MutableList
ïŒãšããŠå®çŸ©ãããŠããŸãããäžå€ã®
List
ã€ã³ã¿ãŒãã§ãŒã¹ãšããŠæäŸãããŠããããšã«æ³šæããŠãã ããã ããã¯Kotlinã§æ©èœããŸãããçµæã®ãªã¹ãã倿Žå¯èœãªã€ã³ã¿ãŒãã§ã€ã¹ã«å€æãããããããŸããŸãªå€æŽãå ãããããããšã劚ãããã®ããªãããããã®ã¢ãããŒãã¯å®å
šã§ã¯ãããŸãã-Javaã³ãŒãããã¯ã©ã¹ã«ã¢ã¯ã»ã¹ããå ŽåïŒ
List
ã€ã³ã¿ãŒãã§ã€ã¹ã倿Žå¯èœãªå ŽåïŒãããã¯ç¹ã«ç°¡åã§ãã ã³ã¬ã¯ã·ã§ã³ãä¿è·ããæãåŠæ³çãªæ¹æ³ã¯ã次ã®ãããªããšã§ãã
fun getTraits(): List<Trait> = Collections.unmodifiableList(traits)
ããããç§ãã¡ã¯åé¡ã«è¿ã¥ããŠããã®ã«ããã»ã©æ
éã§ã¯ãããŸããïŒãã ããèŠåãããŠããŸãïŒã
ã²ãŒã ã¡ã«ãã¯ã¹ã®ç¹æ§ã«ããã
Obstacle
ã¯ã©ã¹ã¯è¿œå ãã£ãŒã«ããããç¹ã§å¯Ÿå¿ããã¯ã©ã¹ãšã¯ç°ãªããŸããããããã«çŠç¹ãåãããŸããã
è
åšã«ãŒãïŒããã³èšèšææžãæ³šææ·±ãèªãã å Žåããããã¯ã«ãŒãã§ããããšãå¿ããªãã§ãã ããïŒã¯ã
Deck
ã¯ã©ã¹ã§è¡šããããããã«çµåãããŸãã
class Deck<E: Threat> { private val cards = LinkedList<E>() val size get() = cards.size fun addToTop(card: E) = cards.addFirst(card) fun addToBottom(card: E) = cards.addLast(card) fun revealTop(): E = cards.first fun drawFromTop(): E = cards.removeFirst() fun shuffle() = cards.shuffle() fun clear() = cards.clear() fun examine() = cards.toList() }
ããã§ã¯ãã¯ã©ã¹ããã©ã¡ãŒã¿ãŒåãããé©åãªã¡ãœããã䜿çšããŠæ··åã§ããé åºä»ããªã¹ãïŒãŸãã¯åæ¹åãã¥ãŒïŒãå«ãŸããŠããããšãé€ããŠãç°åžžãªãã®ã¯ãããŸããã æµãé害ç©ã®ãããã¯ãæåéãäžç¬ã®ãã¡ã«èæ
®ã«å
¥ããå¿
èŠããããŸã...
...
Location
ã¯ã©ã¹ã®åã€ã³ã¹ã¿ã³ã¹ã¯ãããŒããŒãã¹ã¯ãªããã®äžéšãšããŠèšªåããªããã°ãªããªãäžæã®å Žæãèšè¿°ããŸãã
class Location { var name: String = "" var description: String = "" var isOpen = true var closingDifficulty = 0 lateinit var bag: Bag var villain: Villain? = null lateinit var enemies: Deck<Enemy> lateinit var obstacles: Deck<Obstacle> private val specialRules = mutableListOf<SpecialRule>() fun addSpecialRule(rule: SpecialRule) = specialRules.add(rule) fun getSpecialRules() = specialRules }
åå°åã«ã¯ãååã説æãééã®é£æåºŠãããªãŒãã³/ã¯ããŒãºãã®ãµã€ã³ããããŸãã ããã®ã©ããã«ãæªåœ¹ãæœãã§ããå¯èœæ§ããããŸãïŒãŸãã¯ãæœãã§ããªãå¯èœæ§ãããããã®çµæãšããŠã
villain
ããããã£ã
null
ãªãå Žåããã
null
ïŒã åãšãªã¢ã«ã¯ããã¥ãŒãã®å
¥ã£ãããã°ãšè
åšã®ãããããããããŸãã å°åœ¢ã«ã¯ãç¬èªã®ã²ãŒã æ©èœïŒ
SpecialRule
ïŒã
SpecialRule
ããšãã§ããŸããããã¯ãè
åšã®ããããã£ãšåæ§ã«ãã²ãŒã ãã¬ã€ã«å€æ§æ§ã远å ããŸãã ã芧ã®ãšãããè¿ãå°æ¥ã«å®è£
ããäºå®ããªãå Žåã§ããå°æ¥ã®æ©èœã®åºç€ãæ§ç¯ããŠããŸãïŒå®éã«ã¯ãã¢ããªã³ã°æ®µéãå¿
èŠã§ãïŒã
æåŸã«ãã¹ã¯ãªããïŒ
Scenario
ã¯ã©ã¹ïŒãå®è£
ããŸãã
class Scenario { var name = "" var description = "" var level = 0 var initialTimer = 0 private val allySkills = mutableListOf<AllySkill>() private val specialRules = mutableListOf<SpecialRule>() fun addAllySkill(skill: AllySkill) = allySkills.add(skill) fun getAllySkills(): List<AllySkill> = Collections.unmodifiableList(allySkills) fun addSpecialRule(rule: SpecialRule) = specialRules.add(rule) fun getSpecialRules(): List<SpecialRule> = Collections.unmodifiableList(specialRules) }
åã·ããªãªã¯ãã¿ã€ããŒã®ã¬ãã«ãšåæå€ã«ãã£ãŠç¹åŸŽä»ããããŸãã åã«èŠããã®ãšåæ§ã«ãç¹å¥ãªã«ãŒã«ïŒ
specialRules
ïŒãšåçè
ã®ã¹ãã«ãèšå®ãããŸãïŒèæ
®ããéããŸãïŒã ã¹ã¯ãªããã«ã¯ãå ŽæïŒ
Location
ã¯ã©ã¹ã®ãªããžã§ã¯ãïŒã®ãªã¹ããå«ããã¹ãã ãšæããããããŸããããè«ççã«ã¯ãããã¯æ¬åœã«ããã§ãã ããããåŸã§èŠãããããã«ããã®ãããªæ¥ç¶ã¯ã©ãã§ã䜿çšãããæè¡çãªå©ç¹ããããŸããã
ãããŸã§ã«æ€èšãããã¹ãŠã®ã¯ã©ã¹ã
model
ããã±ãŒãžã«å«ãŸããŠããããšãæãåºããŠãã ãããåäŸã®é ã壮倧ãªããã¡ãã®æŠããèŠè¶ããŠãããŒãã«ã®è¡šé¢ã«å
µå£«ãé
眮ããŸããã ãããŠä»ãããã€ãã®çã¿ã䌎ãç¬éã®åŸãæé«åžä»€å®ã®åå³ã§ãç§ãã¡ã¯ããã¡ããæŒãåãããã²ãŒã ãã¬ã€ã®çµæã楜ããã§ãæŠãã«çªå
¥ããŸãã ãããããã®åã«ãåãæ±ºãèªäœã«ã€ããŠå°ã説æããŸãã
ããã...ã7çªç®ã®ã¹ãããã ãã¿ãŒã³ãšãžã§ãã¬ãŒã¿ãŒ
以åã«èæ
®ããããªããžã§ã¯ããçæããããã»ã¹ããããšãã°ãã±ãŒã·ã§ã³ïŒå°åœ¢ïŒã«ãªãããšãå°ãæ³åããŠã¿ãŸããããã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãäœæããLocation
ãã£ãŒã«ããå€ã§åæåããå¿
èŠããããŸãããããã£ãŠãã²ãŒã ã§äœ¿çšããããŒã«ãªãã£ããšã«å¿
èŠã§ãããã ããåŸ
ã£ãŠãã ãããåå Žæã«ã¯ããã°ãå¿
èŠã§ããããããçæããå¿
èŠããããŸãããŸããããã°ã«ã¯ãã¥ãŒãããããŸã-ãããã¯ã察å¿ããã¯ã©ã¹ïŒDie
ïŒã®ã€ã³ã¹ã¿ã³ã¹ã§ããããŸããããã¯æµãé害ç©ã®ããšã§ã¯ãããŸãã-ãããã¯äžè¬çã«ãããã«éããããå¿
èŠããããŸãããŸããæªåœ¹ã¯å°åœ¢èªäœã決å®ããã®ã§ã¯ãªããã·ããªãªã®æ©èœã1ã¬ãã«äžã«é
眮ããŸããããŠãããªãã¯ãã€ã³ããåŸãã
äžèšã®ãœãŒã¹ã³ãŒãã¯æ¬¡ã®ããã«ãªããŸãã val location = Location().apply { name = "Some location" description = "Some description" isOpen = true closingDifficulty = 4 bag = Bag().apply { put(Die(Die.Type.PHYSICAL, 4)) put(Die(Die.Type.SOMATIC, 4)) put(Die(Die.Type.MENTAL, 4)) put(Die(Die.Type.ENEMY, 6)) put(Die(Die.Type.OBSTACLE, 6)) put(Die(Die.Type.VILLAIN, 6)) } villain = Villain().apply { name = "Some villain" description = "Some description" addTrait(Trait.MODIFIER_PLUS_ONE) } enemies = Deck<Enemy>().apply { addToTop(Enemy().apply { name = "Some enemy" description = "Some description" }) addToTop(Enemy().apply { name = "Other enemy" description = "Some description" }) shuffle() } obstacles = Deck<Obstacle>().apply { addToTop(Obstacle(1, Die.Type.PHYSICAL, Die.Type.VERBAL).apply { name = "Some obstacle" description = "Some Description" }) } }
ããã¯ãKotlinèšèªãšèšèšã®ãããã§ãããapply{}
ãŸããJavaã§ã¯ãã³ãŒãã¯2åæ±ãã«ãããªããŸããããã«ãç§ãã¡ãèšã£ãããã«å€ãã®å Žæãããããããã®ã»ãã«ãã·ããªãªãåéºããããŠã¹ãã«ãšç¹åŸŽãæã€ããŒããŒãããŸã-äžè¬ã«ãã²ãŒã ãã¶ã€ããŒãããããšããããŸãããã ããã²ãŒã ãã¶ã€ããŒã¯ã³ãŒããèšè¿°ããªããããã²ãŒã ã®äžçã§ããããªå€åããã£ããšãã«ãããžã§ã¯ããåã³ã³ãã€ã«ããã®ã¯äžäŸ¿ã§ããããã§ã¯ãæèœãªããã°ã©ããŒã¯ãã¯ã©ã¹ã³ãŒããããªããžã§ã¯ãã®èšè¿°ãåé¢ããå¿
èŠãããããšã«å察ããŸã-çæ³çã«ã¯ãåŸè
ã®ã€ã³ã¹ã¿ã³ã¹ã¯ãå¿
èŠã«å¿ããŠåè
ã«åºã¥ããŠåçã«çæãããŸãããã®ãããªå³é¢ãå®è£
ãããããããã³ãã¬ãŒããšåŒã¶ã ãã§ãç¹å¥ãªã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãšããŠè¡šããŸãããã®ãããªãã¿ãŒã³ã䜿çšãããšãç¹å¥ãªããã°ã©ã ã³ãŒãïŒãžã§ãã¬ãŒã¿ãŒïŒãåè¿°ã®ã¢ãã«ããæçµçãªãªããžã§ã¯ããäœæããŸãããã³ãã¬ãŒããããªããžã§ã¯ããçæããããã»ã¹ ãããã£ãŠããªããžã§ã¯ãã®ã¯ã©ã¹ããšã«ããã³ãã¬ãŒãã€ã³ã¿ãŒãã§ã€ã¹ãšãžã§ãã¬ãŒã¿ã¯ã©ã¹ã®2ã€ã®æ°ãããšã³ãã£ãã£ãå®çŸ©ããå¿
èŠããããŸãããããŠãããªãã®éã®ãªããžã§ã¯ããèç©ãããŠããããã倿°ã®ãšã³ãã£ãã£ããããŸã...äžåã§ãïŒæ°ãæ£ãããªãããã«ãæ·±ãåŒåžããæ³šææ·±ãèããŠãã ããããŸããå³ã«ã¯ã²ãŒã ã¯ãŒã«ãã®ãã¹ãŠã®ãªããžã§ã¯ãã衚瀺ãããŠããããã§ã¯ãªããã¡ã€ã³ã®ãªããžã§ã¯ãã®ã¿ã衚瀺ãããŠããŸãã第äºã«ãäžå¿
èŠãªè©³çްã§åè·¯ãéè² è·ã«ããªãããã«ãä»ã®å³ã§ãã§ã«è¿°ã¹ãæ¥ç¶ã®ããã€ãã¯çç¥ãããŸããããã¥ãŒãã®çæ-ã·ã³ãã«ãªãã®ããå§ããŸãããã ãã©ãïŒ -ããªãã¯èšããŸãã -ã³ã³ã¹ãã©ã¯ã¿ãŒãè¶³ããŸãããïŒã¯ããã¯ããåããã®ã§ãã¿ã€ããšãµã€ãºããããŸãããããããååã§ã¯ãããŸãããå®éãå€ãã®å ŽåïŒã«ãŒã«ãèªãã§ïŒããã¥ãŒãã¯ä»»æã®éã§ä»»æã«çæããå¿
èŠããããŸãïŒããšãã°ããéãŸãã¯ç·ã®1ã3åã®ãã¥ãŒããïŒãããã«ãã¹ã¯ãªããã®è€éãã®ã¬ãã«ã«å¿ããŠãµã€ãºãéžæããŸãããããã£ãŠãç¹å¥ãªã€ã³ã¿ãŒãã§ã€ã¹ãå°å
¥ããŸãDieTypeFilter
ã interface DieTypeFilter { fun test(type: Die.Type): Boolean }
ãã®ã€ã³ã¿ãŒãã§ã€ã¹ã®ããŸããŸãªå®è£
ã¯ããã¥ãŒãã®ã¿ã€ããç°ãªãã«ãŒã«ã»ããïŒæãæµ®ãã¶ãã®ïŒã«å¯Ÿå¿ããŠãããã©ããããã§ãã¯ããŸããããšãã°ãã¿ã€ããå³å¯ã«æå®ãããå€ïŒãéãïŒãŸãã¯å€ã®ç¯å²ïŒãéãé»ãç·ãïŒã«å¯Ÿå¿ãããã©ããããŸãã¯ãå察ã«ãæå®ãããã¿ã€ã以å€ã®ãã¹ãŠã®ã¿ã€ãã«å¯Ÿå¿ããŸãïŒããããã©ããªå Žåã§ãçœã§ãªãã£ãå Žåã-äœã§ããããã ãïŒãå¿
èŠãªç¹å®ã®å®è£
ãäºåã«æç¢ºã§ã¯ãªãå Žåã§ããéèŠã§ã¯ãããŸãã-åŸã§è¿œå ã§ããŸãããã·ã¹ãã ã¯ãããäžæããŸããïŒå€æ
æ§ãèŠããŠããŸããïŒïŒã class SingleDieTypeFilter(val type: Die.Type): DieTypeFilter { override fun test(type: Die.Type) = (this.type == type) } class InvertedSingleDieTypeFilter(val type: Die.Type): DieTypeFilter { override fun test(type: Die.Type) = (this.type != type) } class MultipleDieTypeFilter(vararg val types: Die.Type): DieTypeFilter { override fun test(type: Die.Type) = (type in types) } class InvertedMultipleDieTypeFilter(vararg val types: Die.Type): DieTypeFilter { override fun test(type: Die.Type) = (type !in types) }
ãã¥ãŒãã®ãµã€ãºãä»»æã«èšå®ãããŸãããããã«ã€ããŠã¯åŸã§è©³ãã説æããŸãããããŸã§ã®éããã¥ãŒããžã§ãã¬ãŒã¿ãŒïŒDieGenerator
ïŒãäœæããŸããããã¯ãã¯ã©ã¹ã³ã³ã¹ãã©ã¯ã¿ãŒãšã¯ç°ãªãDie
ããã¥ãŒãã®æç€ºçãªã¿ã€ããšãµã€ãºã§ã¯ãªãããã£ã«ã¿ãŒãšè€éãã®ã¬ãã«ãåãå
¥ããŸãã private val DISTRIBUTION_LEVEL1 = intArrayOf(4, 4, 4, 4, 6, 6, 6, 6, 8) private val DISTRIBUTION_LEVEL2 = intArrayOf(4, 6, 6, 6, 6, 8, 8, 8, 8, 10) private val DISTRIBUTION_LEVEL3 = intArrayOf(6, 8, 8, 8, 10, 10, 10, 10, 12, 12, 12) private val DISTRIBUTIONS = arrayOf( intArrayOf(4), DISTRIBUTION_LEVEL1, DISTRIBUTION_LEVEL2, DISTRIBUTION_LEVEL3 ) fun getMaxLevel() = DISTRIBUTIONS.size - 1 fun generateDie(filter: DieTypeFilter, level: Int) = Die(generateDieType(filter), generateDieSize(level)) private fun generateDieType(filter: DieTypeFilter): Die.Type { var type: Die.Type do { type = Die.Type.values().random() } while (!filter.test(type)) return type } private fun generateDieSize(level: Int) = DISTRIBUTIONS[if (level < 1 || level > getMaxLevel()) 0 else level].random()
Javaã§ã¯ããããã®ã¡ãœããã¯éçã«ãªããŸãããKotlinãæ±ãããããã®ãããªã¯ã©ã¹ã¯å¿
èŠãããŸãããããã¯ã以äžã§èª¬æããä»ã®ãžã§ãã¬ãŒã¿ãŒã«ãåœãŠã¯ãŸããŸãïŒããã«ãããããããè«çã¬ãã«ã§ã¯ãã¯ã©ã¹ã®æŠå¿µã䜿çšããŸãïŒã2ã€ã®ãã©ã€ããŒãã¡ãœããã¯ããã¥ãŒãã®ã¿ã€ããšãµã€ãºãåå¥ã«çæããŸããããããã«ã€ããŠè峿·±ãããšãèšããŸãããã®ã¡ãœããgenerateDieType()
ã¯ã次ã®ããã«ãã£ã«ã¿ãŒãæž¡ãããšã§ç¡éã«ãŒãã«é§åã§ããŸãã override fun test(filter: DieTypeFilter) = false
ïŒäœå®¶ã¯ãã¹ããŒãªãŒäžã«ç»å Žäººç©èªèº«ã芳客ãæãå Žåãè«ççãªççŸããæãåºãã穎ã空ããããšãã§ãããšåŒ·ãä¿¡ããŠããŸãïŒããã®ã¡ãœããgenerateDieSize()
ã¯ãé
åïŒåã¬ãã«ã«1ã€ïŒã®åœ¢åŒã§æå®ãããååžã«åºã¥ããŠãç䌌ã©ã³ãã ãµã€ãºãçæããŸããèåŸãéæã¡ã«ãªã£ãŠãã«ãã«ã©ãŒã®ãã¬ã€ãããã¯ã賌å
¥ãããšããµã€ã³ãããã¬ã€ããããšãã§ããŸããããªããªãããããããã©ã³ãã ã«ããã°ãéããæ¹æ³ãããããªãããã§ãïŒé£äººã«å°ããŠãã®å Žã§èãåãã以å€ã¯ïŒãããã¯éããŸã«ã·ã£ããã«ã§ããã«ãŒãã®ãããã§ã¯ãªããç¹å¥ãªã¡ã«ããºã ãšããã€ã¹ãå¿
èŠã§ãã誰ããã¢ã€ãã¢ãæã£ãŠããïŒãããŠã圌ããã®æç¹ãŸã§èªãã§ããå¿èãæã£ãŠããïŒå Žåã¯ãã³ã¡ã³ãã§å
±æããŠãã ããããããŠãããã°ã«ã€ããŠè©±ããŠããã®ã§ãããã°çšã®ãã³ãã¬ãŒããéçºããŸããããªãã®ä»²éãšã¯ç°ãªãããã®ãã³ãã¬ãŒãïŒBagTemplate
ïŒã¯ç¹å®ã®ã¯ã©ã¹ã«ãªããŸããä»ã®ãã³ãã¬ãŒããå«ãŸããŠããŸã-ããããããPlan
1ã€ä»¥äžã®ãã¥ãŒãïŒä»¥åã«äœæãããèŠä»¶ãèŠããŠããŸããïŒïŒãããã°ã«è¿œå ãããã«ãŒã«ïŒãŸãã¯ïŒãèšè¿°ããŸãã class BagTemplate { class Plan(val minQuantity: Int, val maxQuantity: Int, val filter: DieTypeFilter) val plans = mutableListOf<Plan>() fun addPlan(minQuantity: Int, maxQuantity: Int, filter: DieTypeFilter) { plans.add(Plan(minQuantity, maxQuantity, filter)) } }
åãã©ã³ã¯ããã¥ãŒãã®ã¿ã€ãã®ãã¿ãŒã³ãšããã®ãã¿ãŒã³ãæºãããã¥ãŒãã®æ°ïŒæå°ããã³æå€§ïŒãå®çŸ©ããŸãããã®ã¢ãããŒãã®ãããã§ã掟æãªã«ãŒã«ã«åŸã£ãŠããã°ãçæããããšãã§ããŸãïŒãããŠãç§ã®é£äººã¯ãã£ã±ããšç§ãå©ããããšãæåŠããã®ã§ãç§ã¯ç§ã®å€ã幎霢ã§åã³æ¿ããæ³£ããŸãïŒããã®ãããªãã®ïŒ
private fun realizePlan(plan: BagTemplate.Plan, level: Int): Array<Die> { val count = (plan.minQuantity..plan.maxQuantity).shuffled().last() return (1..count).map { generateDie(plan.filter, level) }.toTypedArray() } fun generateBag(template: BagTemplate, level: Int): Bag { return template.plans.asSequence() .map { realizePlan(it, level) } .fold(Bag()) { b, d -> b.put(*d); b } } }
ç§ãšåãããã«ãããªãããã®ãã¹ãŠã®æ©èœäž»çŸ©ã«ç²ããŠãããªããèªåãç· ããŠãã ãã-ããã¯æªåããã ãã§ãããããããã®åŸãã€ã³ã¿ãŒãããäžã®å€ãã®äžæçãªãã¥ãŒããªã¢ã«ãšã¯ç°ãªããå®éã®çè§£å¯èœãªäž»é¡é åã«é¢é£ããŠãããŸããŸãªå·§åŠãªæ¹æ³ã®äœ¿çšãç ç©¶ããæ©äŒããããŸããããèªäœã§ã¯ãããã°ã¯ãã£ãŒã«ãã«æšªãããããšã¯ãããŸãã-ããªãã¯ããŒããŒãšå Žæã«ããããäžããå¿
èŠããããŸããåŸè
ããå§ããŸãããã interface LocationTemplate { val name: String val description: String val bagTemplate: BagTemplate val basicClosingDifficulty: Int val enemyCardsCount: Int val obstacleCardsCount: Int val enemyCardPool: Collection<EnemyTemplate> val obstacleCardPool: Collection<ObstacleTemplate> val specialRules: List<SpecialRule> }
Kotlinèšèªã§ã¯ãã¡ãœããã®ä»£ããã«get()
ã€ã³ã¿ãŒãã§ã€ã¹ããããã£ã䜿çšã§ããŸã-ããã¯ã¯ããã«ç°¡æœã§ããããã°ãã³ãã¬ãŒãã«ã€ããŠã¯ãã§ã«ããç¥ã£ãŠããŸããæ®ãã®æ¹æ³ãæ€èšããŠãã ããããã®ããããã£basicClosingDifficulty
ã¯ãå°åœ¢ãéããããã®ãã§ãã¯ã®åºæ¬çãªè€éããèšå®ããŸããããã§ãããåºæ¬ããšã¯ãæçµçãªè€éããã·ããªãªã®ã¬ãã«ã«äŸåããããšã®ã¿ãæå³ãããã®æ®µéã§ã¯äžæã§ããããã«ãæµãšé害ç©ïŒããã³åæã«æªåœ¹ïŒã®ãã¿ãŒã³ãå®çŸ©ããå¿
èŠããããŸããããã«ããã³ãã¬ãŒãã§èª¬æãããŠããããŸããŸãªæµãé害ç©ããããã¹ãŠã䜿çšãããããã§ã¯ãªããéãããæ°ã ãã䜿çšãããŸãïŒãªãã¬ã€å€ãå¢ããããïŒãSpecialRule
ãšãªã¢ã®ç¹å¥ãªã«ãŒã«ïŒïŒã¯åçŽãªåæïŒenum class
ïŒã«ãã£ãŠå®è£
ããããããå¥ã®ãã³ãã¬ãŒãã¯å¿
èŠãããŸããã interface EnemyTemplate { val name: String val description: String val traits: List<Trait> } interface ObstacleTemplate { val name: String val description: String val tier: Int val dieTypes: Array<Die.Type> val traits: List<Trait> } interface VillainTemplate { val name: String val description: String val traits: List<Trait> }
ãããŠããžã§ãã¬ãŒã¿ãŒã«åã
ã®ãªããžã§ã¯ãã ãã§ãªããããããå«ããããå
šäœãäœæãããŸãã fun generateVillain(template: VillainTemplate) = Villain().apply { name = template.name description = template.description template.traits.forEach { addTrait(it) } } fun generateEnemy(template: EnemyTemplate) = Enemy().apply { name = template.name description = template.description template.traits.forEach { addTrait(it) } } fun generateObstacle(template: ObstacleTemplate) = Obstacle(template.tier, *template.dieTypes).apply { name = template.name description = template.description template.traits.forEach { addTrait(it) } } fun generateEnemyDeck(types: Collection<EnemyTemplate>, limit: Int?): Deck<Enemy> { val deck = types .map { generateEnemy(it) } .shuffled() .fold(Deck<Enemy>()) { d, c -> d.addToTop(c); d } limit?.let { while (deck.size > it) deck.drawFromTop() } return deck } fun generateObstacleDeck(templates: Collection<ObstacleTemplate>, limit: Int?): Deck<Obstacle> { val deck = templates .map { generateObstacle(it) } .shuffled() .fold(Deck<Obstacle>()) { d, c -> d.addToTop(c); d } limit?.let { while (deck.size > it) deck.drawFromTop() } return deck }
ãããã«å¿
èŠä»¥äžã®ã«ãŒããããå ŽåïŒãã©ã¡ãŒã¿ãŒlimit
ïŒãããããåé€ããŸãããã¥ãŒããšã«ãŒãã®ããã¯ã§ããã°ãçæã§ããã®ã§ãæçµçã«å°åœ¢ãäœæã§ããŸãã fun generateLocation(template: LocationTemplate, level: Int) = Location().apply { name = template.name description = template.description bag = generateBag(template.bagTemplate, level) closingDifficulty = template.basicClosingDifficulty + level * 2 enemies = generateEnemyDeck(template.enemyCardPool, template.enemyCardsCount) obstacles = generateObstacleDeck(template.obstacleCardPool, template.obstacleCardsCount) template.specialRules.forEach { addSpecialRule(it) } }
ç« ã®åé ã§ã³ãŒãã§æç€ºçã«èšå®ããå°åœ¢ã¯ãå®å
šã«ç°ãªãå€èгã«ãªããŸãã class SomeLocationTemplate: LocationTemplate { override val name = "Some location" override val description = "Some description" override val bagTemplate = BagTemplate().apply { addPlan(1, 1, SingleDieTypeFilter(Die.Type.PHYSICAL)) addPlan(1, 1, SingleDieTypeFilter(Die.Type.SOMATIC)) addPlan(1, 2, SingleDieTypeFilter(Die.Type.MENTAL)) addPlan(2, 2, MultipleDieTypeFilter(Die.Type.ENEMY, Die.Type.OBSTACLE)) } override val basicClosingDifficulty = 2 override val enemyCardsCount = 2 override val obstacleCardsCount = 1 override val enemyCardPool = listOf( SomeEnemyTemplate(), OtherEnemyTemplate() ) override val obstacleCardPool = listOf( SomeObstacleTemplate() ) override val specialRules = emptyList<SpecialRule>() } class SomeEnemyTemplate: EnemyTemplate { override val name = "Some enemy" override val description = "Some description" override val traits = emptyList<Trait>() } class OtherEnemyTemplate: EnemyTemplate { override val name = "Other enemy" override val description = "Some description" override val traits = emptyList<Trait>() } class SomeObstacleTemplate: ObstacleTemplate { override val name = "Some obstacle" override val description = "Some description" override val traits = emptyList<Trait>() override val tier = 1 override val dieTypes = arrayOf( Die.Type.PHYSICAL, Die.Type.VERBAL ) } val location = generateLocation(SomeLocationTemplate(), 1)
ã·ããªãªã®çæãåæ§ã«è¡ãããŸãã interface ScenarioTemplate { val name: String val description: String val initialTimer: Int val staticLocations: List<LocationTemplate> val dynamicLocationsPool: List<LocationTemplate> val villains: List<VillainTemplate> val specialRules: List<SpecialRule> fun calculateDynamicLocationsCount(numberOfHeroes: Int) = numberOfHeroes + 2 }
èŠåã«åŸã£ãŠãåçã«çæãããå Žæã®æ°ã¯ããŒããŒã®æ°ã«äŸåããŸããã€ã³ã¿ãŒãã§ã€ã¹ã¯ãå¿
èŠã«å¿ããŠç¹å®ã®å®è£
ã§åå®çŸ©ã§ããæšæºã®èšç®é¢æ°ãå®çŸ©ããŸãããã®èŠä»¶ã«ãããã·ããªãªãžã§ãã¬ãŒã¿ãŒã¯ãããã®ã·ããªãªã®å°åœ¢ãçæããŸããåãå Žæã§æªåœ¹ã¯å°åéã§ã©ã³ãã ã«åæ£ãããŸãã fun generateScenario(template: ScenarioTemplate, level: Int) = Scenario().apply { name =template.name description = template.description this.level = level initialTimer = template.initialTimer template.specialRules.forEach { addSpecialRule(it) } } fun generateLocations(template: ScenarioTemplate, level: Int, numberOfHeroes: Int): List<Location> { val locations = template.staticLocations.map { generateLocation(it, level) } + template.dynamicLocationsPool .map { generateLocation(it, level) } .shuffled() .take(template.calculateDynamicLocationsCount(numberOfHeroes)) val villains = template.villains .map(::generateVillain) .shuffled() locations.forEachIndexed { index, location -> if (index < villains.size) { location.villain = villains[index] location.bag.put(generateDie(SingleDieTypeFilter(Die.Type.VILLAIN), level)) } } return locations }
å€ãã®ç±å¿ãªèªè
ã¯ãã¯ã©ã¹ã®ãœãŒã¹ã³ãŒãã§ã¯ãªããäžéšã®ããã¹ããã¡ã€ã«ïŒã¹ã¯ãªããïŒã«ãã³ãã¬ãŒããä¿åããå¿
èŠãããããšã«å察ããã§ããããåæããŸããåžœåãè±ããŸãããé ã«ç°ãæ¯ããããŸãããäžæ¹ã仿¹ã«å¹²æžããªãããã§ããå¿
èŠã«å¿ããŠããã³ãã¬ãŒãã®ç¹å¥ãªå®è£
ãå®çŸ©ããã ãã§ããã®ããããã£å€ã¯å€éšãã¡ã€ã«ããããŒããããŸããããããã®çæããã»ã¹ã¯ã1ã€ã®iotaã倿ŽããŸããããŸãã圌ãã¯äœãå¿ããŠããªãããã§ã...ãããããã§ããããŒããŒ-圌ããçæããå¿
èŠããããŸããããã¯åœŒãèªèº«ã®ãã³ãã¬ãŒããå¿
èŠãšããããšãæå³ããŸããæ¬¡ã«äŸã瀺ããŸãã interface HeroTemplate { val type: Hero.Type val initialHandCapacity: Int val favoredDieType: Die.Type val initialDice: Collection<Die> val initialSkills: List<SkillTemplate> val dormantSkills: List<SkillTemplate> fun getDiceCount(type: Die.Type): Pair<Int, Int>? }
ãããŠãããã«2ã€ã®å¥åŠãªç¹ã«æ°ä»ããŸãããŸãããã³ãã¬ãŒãã䜿çšããŠããã°ãšãã¥ãŒããçæããŸããããªãã§ïŒ
ã¯ããããŒããŒã®ã¿ã€ãïŒã¯ã©ã¹ïŒããšã«åæãã¥ãŒãã®ãªã¹ããå³å¯ã«å®çŸ©ãããŠãããããäœæããã»ã¹ãè€éã«ããæå³ã¯ãããŸããã第äºã«ãgetDiceCount()
ããã¯ã©ããªçš®é¡ã®ããã§ããïŒèœã¡çããŠããããã¯DiceLimit
ãã¥ãŒãã®å¶éãèšå®ãããã®ã§ãããããŠããããã®ãã³ãã¬ãŒãã¯ãç¹å®ã®å€ãããæç¢ºã«èšé²ããããããªå¥åŠãªåœ¢ã§éžæãããŸãããäŸããèªåã®ç®ã§ç¢ºãããŠãã ããïŒ class BrawlerHeroTemplate : HeroTemplate { override val type = Hero.Type.BRAWLER override val favoredDieType = PHYSICAL override val initialHandCapacity = 4 override val initialDice = listOf( Die(PHYSICAL, 6), Die(PHYSICAL, 6), Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(SOMATIC, 6), Die(SOMATIC, 4), Die(SOMATIC, 4), Die(SOMATIC, 4), Die(MENTAL, 4), Die(VERBAL, 4), Die(VERBAL, 4) ) override fun getDiceCount(type: Die.Type) = when (type) { PHYSICAL -> 8 to 12 SOMATIC -> 4 to 7 MENTAL -> 1 to 2 VERBAL -> 2 to 4 else -> null } override val initialSkills = listOf( HitSkillTemplate() ) override val dormantSkills = listOf<SkillTemplate>() } class HunterHeroTemplate : HeroTemplate { override val type = Hero.Type.HUNTER override val favoredDieType = SOMATIC override val initialHandCapacity = 5 override val initialDice = listOf( Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(PHYSICAL, 4), Die(SOMATIC, 6), Die(SOMATIC, 6), Die(SOMATIC, 4), Die(SOMATIC, 4), Die(SOMATIC, 4), Die(SOMATIC, 4), Die(SOMATIC, 4), Die(MENTAL, 6), Die(MENTAL, 4), Die(MENTAL, 4), Die(MENTAL, 4), Die(VERBAL, 4) ) override fun getDiceCount(type: Die.Type) = when (type) { PHYSICAL -> 3 to 5 SOMATIC -> 7 to 11 MENTAL -> 4 to 7 VERBAL -> 1 to 2 else -> null } override val initialSkills = listOf( ShootSkillTemplate() ) override val dormantSkills = listOf<SkillTemplate>() }
ãã ãããžã§ãã¬ãŒã¿ãäœæããåã«ãã¹ãã«ã®ãã³ãã¬ãŒããå®çŸ©ããŸãã interface SkillTemplate { val type: Skill.Type val maxLevel: Int val modifier1: Int val modifier2: Int val isActive get() = true } class HitSkillTemplate : SkillTemplate { override val type = Skill.Type.HIT override val maxLevel = 3 override val modifier1 = +1 override val modifier2 = +3 } class ShootSkillTemplate : SkillTemplate { override val type = Skill.Type.SHOOT override val maxLevel = 3 override val modifier1 = +0 override val modifier2 = +2 }
æ®å¿µãªãããæµãå°æ¬ãšåãããã«ãããã§ãªãããã¹ãã«ã䜿ãããšã¯ã§ããŸãããæ°ããã¹ãã«ããšã«ã²ãŒã ã¡ã«ãã¯ã¹ã®æ¡åŒµãå¿
èŠã«ãªããã²ãŒã ãšã³ãžã³ã«æ°ããã³ãŒãã远å ããŸãããã®ç¹ã§ããŒããŒãããå Žåã§ãç°¡åã§ããããããããã®ããã»ã¹ã¯æœè±¡åã§ããŸãããç§ã¯ãŸã æ¹æ³ãæãã€ããŸãããã¯ããæ£çŽã«èšããšããŸã詊ã¿ãããŠããŸããã fun generateSkill(template: SkillTemplate, initialLevel: Int = 1): Skill { val skill = Skill(template.type) skill.isActive = template.isActive skill.level = initialLevel skill.maxLevel = template.maxLevel skill.modifier1 = template.modifier1 skill.modifier2 = template.modifier2 return skill } fun generateHero(type: Hero.Type, name: String = ""): Hero { val template = when (type) { BRAWLER -> BrawlerHeroTemplate() HUNTER -> HunterHeroTemplate() } val hero = Hero(type) hero.name = name hero.isAlive = true hero.favoredDieType = template.favoredDieType hero.hand.capacity = template.initialHandCapacity template.initialDice.forEach { hero.bag.put(it) } for ((t, l) in Die.Type.values().map { it to template.getDiceCount(it) }) { l?.let { hero.addDiceLimit(DiceLimit(t, it.first, it.second, it.first)) } } template.initialSkills .map { generateSkill(it) } .forEach { hero.addSkill(it) } template.dormantSkills .map { generateSkill(it, 0) } .forEach { hero.addDormantSkill(it) } return hero }
ã»ãã®æ°ç¹ãå°è±¡çã§ãããŸããçæã¡ãœããèªäœãããŒããŒã®ã¯ã©ã¹ã«å¿ããŠç®çã®ãã³ãã¬ãŒããéžæããŸãã第äºã«ãååãããã«æå®ããå¿
èŠã¯ãããŸããïŒçææ®µéã§ååãç¥ããªãããšããããŸãïŒã第äžã«ãKotlinã¯åäŸã®ãªãéã®ã·ã³ã¿ãã¯ã¹ã·ã¥ã¬ãŒããããããŸãããå°ãæ¥ãããããªããã¹ããã8ãã²ãŒã ãµã€ã¯ã«
æåŸã«ãæãè峿·±ãããšã«å°éããŸãã-ã²ãŒã ãµã€ã¯ã«ã®å®è£
ã§ããç°¡åã«èšãã°ã圌ãã¯ãã²ãŒã ãäœããããšãå§ããŸãããå€ãã®åæéçºè
ã¯ãã²ãŒã å¶äœãªã©ã®ãã¹ãŠãé€ãããã®æ®µéããæ£ç¢ºã«éå§ããããšããããããŸããç¹ã«ããã¹ãŠã®çš®é¡ã®æå³ã®ãªãå°ããªã¹ããŒã ãpfff ...ããããæ¥ããŸããïŒãŸã æããé ãã§ãïŒããããã£ãŠãããå°ãã¢ããªã³ã°ããŸããã¯ããããäžåºŠãã芧ã®ãšãããã²ãŒã ãµã€ã¯ã«ã®ç¹å®ã®ãã©ã°ã¡ã³ãã¯ãäžèšã§åŒçšãããã®ãããæ¡éãã«å°ãããªã£ãŠããŸããã³ãŒã¹ãç§»è¡ããå°åœ¢ãæ¢çŽ¢ãïŒ2çš®é¡ã®ãã¥ãŒãã®ã¿ã䜿çšããŠäŒè°ã説æããïŒãã¿ãŒã³çµäºæã«ãã¥ãŒããç Žæ£ããããã»ã¹ã®ã¿ãæ€èšããŸãããããŠãã·ããªãªã宿ãããŠè² ããŸããïŒã¯ãããŸã ã²ãŒã ã«åã€ããšã¯ã§ããŸããïŒ-ããããããªãã¯ã©ãã§ããïŒã¿ã€ããŒã¯æ¯ã¿ãŒã³æžå°ãããã®å®äºæã«äœããããå¿
èŠããããŸããããšãã°ãã¡ãã»ãŒãžã衚瀺ããŠã²ãŒã ãçµäºããŸãããã¹ãŠã¯ã«ãŒã«ã«æžãããŠãããšããã§ããããŒããŒãæ»ãã ãå¥ã®ã²ãŒã ãå®äºããå¿
èŠããããŸããã誰ã圌ãã«å±å®³ãå ããããšã¯ãªãã®ã§ããã®ãŸãŸã«ããŸããåã€ããã«ã¯ããã¹ãŠã®ãšãªã¢ãéããå¿
èŠããããŸãããããã1ã€ã ãã§ãã£ãŠãå°é£ã§ãããããã£ãŠããã®ç¬éãæ®ããŸããããã¹ãã¬ãŒããããŠãæå³ããããŸãã-ãšãã»ã³ã¹ãçè§£ããæ®ãã®æéãåŸã§èªç±æéã«çµããããšãéèŠã§ããããŠããªã-ã²ãŒã ãæžãã«è¡ãããªãã®å€¢ã®ïŒããã®ãããæåã«å¿
èŠãªãªããžã§ã¯ããæ±ºå®ããå¿
èŠããããŸããããŒããŒãºã¹ã¯ãªãããå Žæãç§ãã¡ã¯ãã§ã«ãããã®äœæããã»ã¹ãã¬ãã¥ãŒããŸãã-ãããç¹°ãè¿ãããšã¯ããŸãããå°ããªäŸã§äœ¿çšããå°åœ¢ãã¿ãŒã³ã«ã®ã¿æ³šæããŠãã ããã class TestLocationTemplate : LocationTemplate { override val name = "Test" override val description = "Some Description" override val basicClosingDifficulty = 0 override val enemyCardsCount = 0 override val obstacleCardsCount = 0 override val bagTemplate = BagTemplate().apply { addPlan(2, 2, SingleDieTypeFilter(Die.Type.PHYSICAL)) addPlan(2, 2, SingleDieTypeFilter(Die.Type.SOMATIC)) addPlan(2, 2, SingleDieTypeFilter(Die.Type.MENTAL)) addPlan(2, 2, SingleDieTypeFilter(Die.Type.VERBAL)) addPlan(2, 2, SingleDieTypeFilter(Die.Type.DIVINE)) } override val enemyCardPool = emptyList<EnemyTemplate>() override val obstacleCardPool = emptyList<ObstacleTemplate>() override val specialRules = emptyList<SpecialRule>() }
ã芧ã®ããã«ãããã°ã«ã¯ãããžãã£ãããã¥ãŒãïŒéãç·ã玫ãé»è²ãéïŒãããããŸããããã®å°åã«ã¯æµãé害ç©ã¯ãªããæªåœ¹ãå·ã¯èŠã€ãããŸãããç¹å¥ãªã«ãŒã«ããããŸãã-ãããã®å®è£
ã¯éåžžã«äºæ¬¡çã§ããä¿æããããã¥ãŒãã®ããŒãããŸãã¯ææ¢ãã€ã«ãéãç«æ¹äœãå°åœ¢ã®ããã°ã«å
¥ããã®ã§ããã§ãã¯ã§äœ¿çšãããã䜿çšåŸã«ç¹å¥ãªããŒãã«ä¿ç®¡ãããã§ããŸããããã«ã¯ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ã圹ç«ã¡Pile
ãŸãã修食åãã€ãŸãããµã€ã³ãã®çµæã«å ç®ãŸãã¯æžç®ããå¿
èŠãããæ°å€ãåãã¥ãŒãã«ã°ããŒãã«ä¿®é£ŸåãŸãã¯åå¥ã®ä¿®é£Ÿåãå®è£
ã§ããŸãã ïŒããæç¢ºã«ïŒ2çªç®ã®ãªãã·ã§ã³ãéžæãããããåçŽãªã¯ã©ã¹ãäœæããŸãDiePair
ã class DiePair(val die: Die, var modifier: Int = 0)
ãšãªã¢å
ã®ãã£ã©ã¯ã¿ãŒã®å Žæãè¯ãæ¹æ³ã§ããã®ç¬éã¯ç¹å¥ãªæ§é ã䜿çšããŠè¿œè·¡ããå¿
èŠããããŸããããšãã°Map<Location, List<Hero>>
ãåããŒã«ãªãã£ã«çŸåšããããŒããŒã®ãªã¹ããå«ãŸãããã©ãŒã ã®ãããïŒããã³åå¯Ÿã®æ¹æ³-ç¹å®ã®ããŒããŒãããããŒã«ãªãã£ã決å®ããæ¹æ³ïŒãããªãã¯ãã®ã«ãŒããè¡ãããšã«æ±ºããå Žåã¯ãã¯ã©ã¹ã远å ããããšãå¿ããªãã§ãã ããLocation
ã¡ãœããã®å®è£
ãequals()
ããhashCode()
ããŸãããã°ãçç±ã説æããå¿
èŠã¯ãããŸãã- ããã®ãšãªã¢ã¯1ã€ã ãã§ãããããŒããŒã¯ãããã©ãã«ãæ®ããªãã®ã§ãããã«æéãç¡é§ã«ããŸããã䞻人å
¬ã®æã確èªããŸããã²ãŒã ã®éçšã§ãããŒããŒã¯åžžã«ãã§ãã¯ïŒä»¥äžã§èª¬æïŒãå®è¡ããå¿
èŠããããŸããã€ãŸããæãããã¥ãŒããåãåºããããããã¹ããŒïŒã¢ãã£ãã¡ã€ã¢ã远å ïŒããè€æ°ã®ãã¥ãŒããããå Žåã«çµæãéèšïŒèŠçŽãæå€§/æå°ãå¹³åãªã©ïŒããããããã¹ããŒãšæ¯èŒããŸãå¥ã®ãã¥ãŒãïŒãšãªã¢ã®ããã°ããåãåºããããã®ïŒãããã³çµæã«å¿ããŠã次ã®ã¢ã¯ã·ã§ã³ãå®è¡ããŸãããããããŸã第äžã«ã䞻人å
¬ãååçã«ãã¹ãã«åæ Œã§ãããã©ãããã€ãŸããå¿
èŠãªãã¥ãŒããæã«æã£ãŠãããã©ãããçè§£ããå¿
èŠããããŸãããã®ããã«ãã·ã³ãã«ãªã€ã³ã¿ãŒãã§ã€ã¹ãæäŸããŸãHandFilter
ã interface HandFilter { fun test(hand: Hand): Boolean }
ã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£
ã¯ãããŒããŒã®æïŒã¯ã©ã¹ãªããžã§ã¯ãHand
ïŒãå
¥åãšããŠåãåãããã§ãã¯ã®çµæã«å¿ããŠtrue
ã©ã¡ãããè¿ããŸãfalse
ãã²ãŒã ã®ãã©ã°ã¡ã³ãã«ã¯ãåäžã®å®è£
ãå¿
èŠã§ããéãç·ã玫ããŸãã¯é»è²ã®ãã¥ãŒããæºããããå ŽåãããŒããŒã®æã«åãè²ã®ãã¥ãŒãããããã©ããã倿ããå¿
èŠããããŸãã class SingleDieHandFilter(private vararg val types: Die.Type) : HandFilter { override fun test(hand: Hand) = (0 until hand.dieCount).mapNotNull { hand.dieAt(it) }.any { it.type in types } || (Die.Type.ALLY in types && hand.allyDieCount > 0) }
ã¯ããæ©èœäž»çŸ©ã§ããã¢ã¯ãã£ã/éžæãããã¢ã€ãã ã䞻人å
¬ã®æããã¹ãã®å®è¡ã«é©ããŠããããšã確èªããã®ã§ããã¬ã€ã€ãŒã¯ããã®ãã¹ãã«åæ Œãããµã€ã³ãïŒãŸãã¯ãã¥ãŒãïŒãæããéžæããå¿
èŠããããŸãããŸããé©åãªäœçœ®ïŒç®çã®ã¿ã€ãã®ãã¥ãŒããããïŒã匷調衚瀺ïŒãã€ã©ã€ãïŒããå¿
èŠããããŸããæ¬¡ã«ãéžæãããã¥ãŒãã«äœããã®æ¹æ³ã§ããŒã¯ãä»ããå¿
èŠããããŸãããããã®èŠä»¶ã®äž¡æ¹ã«ã€ããŠãã¯ã©ã¹ãé©åHandMask
ã§ãããå®éã«ã¯ãæŽæ°ã®ã»ããïŒéžæãããäœçœ®ã®æ°ïŒãšãããã远å ããã³åé€ããããã®ã¡ãœãããå«ãŸããŠããŸãã class HandMask { private val positions = mutableSetOf<Int>() private val allyPositions = mutableSetOf<Int>() val positionCount get() = positions.size val allyPositionCount get() = allyPositions.size fun addPosition(position: Int) = positions.add(position) fun removePosition(position: Int) = positions.remove(position) fun addAllyPosition(position: Int) = allyPositions.add(position) fun removeAllyPosition(position: Int) = allyPositions.remove(position) fun checkPosition(position: Int) = position in positions fun checkAllyPosition(position: Int) = position in allyPositions fun switchPosition(position: Int) { if (!removePosition(position)) { addPosition(position) } } fun switchAllyPosition(position: Int) { if (!removeAllyPosition(position)) { addAllyPosition(position) } } fun clear() { positions.clear() allyPositions.clear() } }
ãã¯ã€ããã¥ãŒããå¥ã®æã§ä¿ç®¡ãããšãããéªæªãªãã¢ã€ãã¢ã«ã©ã®ããã«èŠããã§ããã®ãããã§ã«è¿°ã¹ãŸããããã®æããã®ããã«ã2ã€ã®ã»ãããåŠçããæç€ºãããåã¡ãœãããè€è£œããå¿
èŠããããŸãã誰ãããã®èŠä»¶ã®å®è£
ãç°¡çŽ åããæ¹æ³ã«ã€ããŠã¢ã€ãã¢ãæã£ãŠããå ŽåïŒããšãã°ã1ã€ã®ã»ããã䜿çšããŸããããã¯ã€ããã¥ãŒãã®å Žåãã€ã³ããã¯ã¹ã¯100ããå§ãŸã-ãŸãã¯ä»ã®åæ§ã«äžæçãªãã®ïŒ-ã³ã¡ã³ãã§å
±æããŸãããšããã§ãããŒãïŒPileMask
ïŒãããã¥ãŒããéžæããã«ã¯ãåæ§ã®ã¯ã©ã¹ãå®è£
ããå¿
èŠããããŸããããã®æ©èœã¯ãã®äŸã®ç¯å²å€ã§ããæããã®ãã¥ãŒãã®éžæããã ãã蚱容å¯èœãªäœçœ®ãã匷調ãããã ãã§ã¯äžååã§ãããã¥ãŒããéžæããããã»ã¹ã§ãã®ã匷調ãã倿ŽããããšãéèŠã§ããã€ãŸãããã¬ãŒã€ãŒãèªåã®æãããã€ã¹ã1ã€ã ãåãå¿
èŠãããå Žåããã®ãã€ã¹ãéžæãããšãä»ã®ãã¹ãŠã®ããžã·ã§ã³ã«ã¢ã¯ã»ã¹ã§ããªããªããŸããããã«ãåæ®µéã§ããã¬ãŒã€ãŒã®ç®æšã®éæãå¶åŸ¡ããå¿
èŠããããŸããã€ãŸããéžæãããã¥ãŒãã1ã€ãŸãã¯å¥ã®ãã¹ãã«åæ Œããã®ã«ååãã©ãããçè§£ããå¿
èŠããããŸãããã®ãããªé£ããã¿ã¹ã¯ã«ã¯ãè€éãªã¯ã©ã¹ã®è€éãªã€ã³ã¹ã¿ã³ã¹ãå¿
èŠã§ãã abstract class HandMaskRule(val hand: Hand) { abstract fun checkMask(mask: HandMask): Boolean abstract fun isPositionActive(mask: HandMask, position: Int): Boolean abstract fun isAllyPositionActive(mask: HandMask, position: Int): Boolean fun getCheckedDice(mask: HandMask): List<Die> { return ((0 until hand.dieCount).filter(mask::checkPosition).map(hand::dieAt)) .plus((0 until hand.allyDieCount).filter(mask::checkAllyPosition).map(hand::allyDieAt)) .filterNotNull() } }
ããªãè€éãªããžãã¯ã§ãããã®ã¯ã©ã¹ãçè§£ã§ããªãå Žåã¯ãçè§£ããŠèš±ããŸãããããŠããŸã 説æããããšããŸãããã®ã¯ã©ã¹ã®å®è£
ã¯ãåžžã«åŠçå¯Ÿè±¡ã®æïŒãªããžã§ã¯ãHand
ïŒãžã®åç
§ãä¿åããŸããåã¡ãœããã¯ãã¹ã¯ïŒHandMask
ïŒãåãåããŸããããã¯ãéžæã®çŸåšã®ç¶æ
ãåæ ããŸãïŒã©ã®äœçœ®ããã¬ãŒã€ãŒã«ãã£ãŠéžæãããã©ã®äœçœ®ãéžæãããªããïŒããã®ã¡ãœããcheckMask()
ã¯ãéžæãããã¥ãŒãããã¹ãã«åæ Œããã®ã«ååãã©ãããå ±åããŸãããã®ã¡ãœããisPositionActive()
ã¯ãç¹å®ã®äœçœ®ã匷調衚瀺ããå¿
èŠããããã©ããããã®äœçœ®ã«ãã¥ãŒãããã¹ãã«è¿œå ïŒãŸãã¯æ¢ã«éžæãããŠãããã¥ãŒããåé€ïŒã§ãããã©ããã瀺ããŸããæ¹æ³isAllyPositionActive()
ã¯çœãµã€ã³ãã§ãåãã§ãïŒã¯ããç§ã¯ç¥ã£ãŠããŸããç§ã¯ã°ãã§ãïŒãããŠããã«ããŒã¡ãœããgetCheckedDice()
åã«ããã¹ã¯ã«å¯Ÿå¿ããæãããã¹ãŠã®ãã¥ãŒãã®ãªã¹ããè¿ããŸã-ããã¯ãããããäžåºŠã«åããããŒãã«ã«æããŠãé¢çœãããã¯ã楜ããããã«å¿
èŠã§ãããã®æœè±¡ã¯ã©ã¹ã®2ã€ã®å®çŸïŒé©ããé©ãïŒïŒãå¿
èŠã§ããæåã¯ãç¹å®ã®ã¿ã€ãïŒçœã§ã¯ãªãïŒã®æ°ãããã¥ãŒããååŸãããšãã«ããã¹ãã«åæ Œããããã»ã¹ãå¶åŸ¡ããŸããèŠããŠããããã«ããã®ãããªãã§ãã¯ã«ã¯ä»»æã®æ°ã®éããã¥ãŒãã远å ã§ããŸãã class StatDieAcquireHandMaskRule(hand: Hand, private val requiredType: Die.Type) : HandMaskRule(hand) { private fun checkedDieCount(mask: HandMask) = (0 until hand.dieCount) .filter(mask::checkPosition) .mapNotNull(hand::dieAt) .count { it.type === requiredType } override fun checkMask(mask: HandMask) = (mask.allyPositionCount == 0 && checkedDieCount(mask) == 1) override fun isPositionActive(mask: HandMask, position: Int) = with(hand.dieAt(position)) { when { mask.checkPosition(position) -> true this == null -> false this.type === Die.Type.DIVINE -> true this.type === requiredType && checkedDieCount(mask) < 1 -> true else -> false } } override fun isAllyPositionActive(mask: HandMask, position: Int) = false }
2çªç®ã®å®è£
ã¯ããè€éã§ããã¿ãŒã³çµäºæã«ãµã€ã³ããå¶åŸ¡ããŸãããã®å Žåã2ã€ã®ãªãã·ã§ã³ãå¯èœã§ããæã®ãã¥ãŒãã®æ°ãæå€§èš±å®¹ãµã€ãºïŒå®¹éïŒãè¶
ããå Žåããã¹ãŠã®äœåãªãã¥ãŒãã«å ããŠãå¿
èŠãªæ°ã®è¿œå ã®ãã¥ãŒããç Žæ£ããå¿
èŠããããŸãããµã€ãºãè¶
ããªãå Žåãäœããªã»ããã§ããŸããïŒãŸãã¯ãå¿
èŠã«å¿ããŠãªã»ããã§ããŸãïŒãç°è²ã®ãµã€ã³ããæšãŠãããšã¯ã§ããŸããã class DiscardExtraDiceHandMaskRule(hand: Hand) : HandMaskRule(hand) { private val minDiceToDiscard = if (hand.dieCount > hand.capacity) min(hand.dieCount - hand.woundCount, hand.dieCount - hand.capacity) else 0 private val maxDiceToDiscard = hand.dieCount - hand.woundCount override fun checkMask(mask: HandMask) = (mask.positionCount in minDiceToDiscard..maxDiceToDiscard) && (mask.allyPositionCount in 0..hand.allyDieCount) override fun isPositionActive(mask: HandMask, position: Int) = when { mask.checkPosition(position) -> true hand.dieAt(position) == null -> false hand.dieAt(position)!!.type == Die.Type.WOUND -> false mask.positionCount < maxDiceToDiscard -> true else -> false } override fun isAllyPositionActive(mask: HandMask, position: Int) = hand.allyDieAt(position) != null }
NezhdanchikïŒã¯ã©ã¹ã«ã¯ã以åã¯ååšããªãã£Hand
ãããããã£ãçªç¶çŸããŸããwoundCount
ãå®è£
ã¯èªåã§æžãããšãã§ããŸããç°¡åã§ããåæã«ç·Žç¿ããŸãããã§ãã¯ã«åæ Œãæçµçã«ãããã«çããŸããããµã€ã³ããæããåãããããããããæããæéã§ãããã¥ãŒãããšã«ããµã€ãºã修食åãã¹ããŒã®çµæãèæ
®ããå¿
èŠããããŸããããã°ããäžåºŠã«åãåºãããšãã§ãããã¥ãŒãã¯1ã€ã ãã§ãããè€æ°ã®ãµã€ã³ããã»ããããŠãããŒã«ã®çµæãéèšã§ããŸããäžè¬çã«ããµã€ã³ãããæœè±¡åããæŠå Žã§ã®è»éã代衚ããŸããããäžæ¹ã§ã¯ãæµãããŸã-圌ã¯ãã äžäººã§ããã圌ã¯åŒ·ããŠå¶æŽã§ããå察ã«ã察æŠçžæã¯åœŒãšåçã®åŒ·ãã§ããããµããŒãããããŸããæŠéã®çµæã¯1åã®çãå°ç«¶ãåãã§æ±ºå®ãããåè
ã¯1人ã ãã«ãªãããšãã§ããŸã...ãã¿ãŸãã äžè¬çãªæŠããã·ãã¥ã¬ãŒãããããã«ãç¹å¥ãªã¯ã©ã¹ãå®è£
ããŸãã class DieBattleCheck(val method: Method, opponent: DiePair? = null) { enum class Method { SUM, AVG_UP, AVG_DOWN, MAX, MIN } private inner class Wrap(val pair: DiePair, var roll: Int) private infix fun DiePair.with(roll: Int) = Wrap(this, roll) private val opponent: Wrap? = opponent?.with(0) private val heroics = ArrayList<Wrap>() var isRolled = false var result: Int? = null val heroPairCount get() = heroics.size fun getOpponentPair() = opponent?.pair fun getOpponentResult() = when { isRolled -> opponent?.roll ?: 0 else -> throw IllegalStateException("Not rolled yet") } fun addHeroPair(pair: DiePair) { if (method == Method.SUM && heroics.size > 0) { pair.modifier = 0 } heroics.add(pair with 0) } fun addHeroPair(die: Die, modifier: Int) = addHeroPair(DiePair(die, modifier)) fun clearHeroPairs() = heroics.clear() fun getHeroPairAt(index: Int) = heroics[index].pair fun getHeroResultAt(index: Int) = when { isRolled -> when { (index in 0 until heroics.size) -> heroics[index].roll else -> 0 } else -> throw IllegalStateException("Not rolled yet") } fun roll() { fun roll(wrap: Wrap) { wrap.roll = wrap.pair.die.roll() } isRolled = true opponent?.let { roll(it) } heroics.forEach { roll(it) } } fun calculateResult() { if (!isRolled) { throw IllegalStateException("Not rolled yet") } val opponentResult = opponent?.let { it.roll + it.pair.modifier } ?: 0 val stats = heroics.map { it.roll + it.pair.modifier } val heroResult = when (method) { DieBattleCheck.Method.SUM -> stats.sum() DieBattleCheck.Method.AVG_UP -> ceil(stats.average()).toInt() DieBattleCheck.Method.AVG_DOWN -> floor(stats.average()).toInt() DieBattleCheck.Method.MAX -> stats.max() ?: 0 DieBattleCheck.Method.MIN -> stats.min() ?: 0 } result = heroResult - opponentResult } }
åãã¥ãŒãã«ã¯ä¿®é£Ÿåãèšå®ã§ããããããªããžã§ã¯ãã«ããŒã¿ãä¿åããŸãDiePair
ãã®ããã§ãã
å®éããããããã¥ãŒããšä¿®é£Ÿåã«å ããŠããã®ã¹ããŒã®çµæãä¿åããå¿
èŠããããŸãïŒãã¥ãŒãèªäœã¯ãã®å€ãçæããŸãããããããã£ã«ã¯ä¿åããŸããïŒããããã£ãŠãåãã¢ãã©ãããŒã§ã©ããããŸãïŒWrap
ïŒãäžçœ®æ³ã«æ³šæããŠãã ããwith
heheãã¯ã©ã¹ã³ã³ã¹ãã©ã¯ã¿ãŒã¯ãéèšã¡ãœããïŒå
éšåæã®ã€ã³ã¹ã¿ã³ã¹Method
ïŒãšå¯ŸæŠçžæïŒååšããªãå ŽåããããŸãïŒãå®çŸ©ããŸããããŒããŒãã¥ãŒãã®ãªã¹ãã¯ãé©åãªæ¹æ³ã䜿çšããŠäœæãããŸãããŸãããã¹ãã«é¢ä¿ãããã¢ãååŸããããã®å€æ°ã®ã¡ãœãããšããããã®ã¹ããŒã®çµæïŒååšããå ŽåïŒãæäŸããŸããã¡ãœããã¯åãã¥ãŒãã®ååã¡ãœãããroll()
åŒã³åºããäžéçµæãä¿åãããã®å®è¡ã®äºå®ããã©ã°ã§ããŒã¯ããŸãisRolled
ãã¹ããŒã®æçµçµæã¯ããã«èšç®ãããªãããšã«æ³šæããŠãã ãããããcalculateResult()
ã«ã¯ç¹å¥ãªã¡ãœãããããããã®çµæã¯ããããã£ã«æçµå€ãæžã蟌ãããšresult
ã§ãããªããããå¿
èŠãªã®ã§ããïŒ
åçãªå¹æã®ãããã¡ãœããroll()
ã¯æ°åå®è¡ããããã®ãã³ã«ãã¥ãŒãã®é¢ã«ç°ãªãå€ã衚瀺ãããŸãïŒå®éã®ããã«ïŒããããŠããã¥ãŒããããŒãã«ã«èœã¡çãããšãã«ã®ã¿ãæçµçµæïŒããŒããŒã®ãã¥ãŒããšå¯ŸæŠçžæã®ãã¥ãŒãã®å€ã®éãïŒã®éåœãåŠã³ãŸããã¹ãã¬ã¹ã軜æžããããã«ãçµæã0ã®å Žåã¯ãã¹ãã«åæ ŒãããšèŠãªãããŸããã²ãŒã ãšã³ãžã³ã®ç¶æ
ãæŽç·Žããããªããžã§ã¯ããæŽçãããããã·ã³ãã«ã«ãªããŸãããã²ãŒã ãšã³ãžã³ã®çŸåšã®ãé²è¡ããããã眮ãããŠããã¹ããŒãžãŸãã¯ãã§ãŒãºãå¶åŸ¡ããå¿
èŠããããšèšãããšã¯ã倧ããªçºèŠã§ã¯ãããŸãããããã«ã¯ç¹å¥ãªåæã圹ç«ã¡ãŸãã enum class GamePhase { SCENARIO_START, HERO_TURN_START, HERO_TURN_END, LOCATION_BEFORE_EXPLORATION, LOCATION_ENCOUNTER_STAT, LOCATION_ENCOUNTER_DIVINE, LOCATION_AFTER_EXPLORATION, GAME_LOSS }
å®éãããã«å€ãã®ãã§ãŒãºããããŸããããã®äŸã§äœ¿çšããããã§ãŒãºã®ã¿ãéžæããŸãããã²ãŒã ãšã³ãžã³ã®ãã§ãŒãºã倿ŽããchangePhaseX()
ã«X
ã¯ãäžèšã®ãªã¹ãã®å€ã§ããã¡ãœããã䜿çšããŸãããããã®æ¹æ³ã§ã¯ããšã³ãžã³ã®ãã¹ãŠã®å
éšå€æ°ã¯ã察å¿ãããã§ãŒãºã®éå§ã«é©ããå€ã«åæžãããŸããããã以éã¯ããã«åæžãããŸããã¡ãã»ãŒãžã²ãŒã ãšã³ãžã³ã®ç¶æ
ãç¶æããã ãã§ã¯äžååã§ãããŠãŒã¶ãŒãäœããã®åœ¢ã§åœŒã«ã€ããŠéç¥ããããšãéèŠã§ã-ãããªããã°ãåŸè
ã¯åœŒã®ç»é¢ã§äœãèµ·ãã£ãŠããããã©ã®ããã«ç¥ãã®ã§ããããïŒãã®ããããã1ã€ãªã¹ããå¿
èŠã§ãã enum class StatusMessage { EMPTY, CHOOSE_DICE_PERFORM_CHECK, END_OF_TURN_DISCARD_EXTRA, END_OF_TURN_DISCARD_OPTIONAL, CHOOSE_ACTION_BEFORE_EXPLORATION, CHOOSE_ACTION_AFTER_EXPLORATION, ENCOUNTER_PHYSICAL, ENCOUNTER_SOMATIC, ENCOUNTER_MENTAL, ENCOUNTER_VERBAL, ENCOUNTER_DIVINE, DIE_ACQUIRE_SUCCESS, DIE_ACQUIRE_FAILURE, GAME_LOSS_OUT_OF_TIME }
ã芧ã®ãšããããã®äŸã®ãã¹ãŠã®å¯èœãªç¶æ
ã¯ããã®åæã®å€ã«ãã£ãŠèšè¿°ãããŠããŸãããããã®ããããã«ã€ããŠãç»é¢ã«è¡šç€ºãããããã¹ãè¡ãæäŸãããŸãïŒãã ããEMPTY
ããã¯ç¹å¥ãªæå³ã§ãïŒããããã«ã€ããŠã¯å°ãåŸã§åŠç¿ããŸããã¢ã¯ã·ã§ã³ãŠãŒã¶ãŒãšã²ãŒã ãšã³ãžã³éã®éä¿¡ã«ã¯ãåçŽãªã¡ãã»ãŒãžã§ã¯äžååã§ãããŸãã圌ãçŸæç¹ã§åãããšãã§ããæåã®ã¢ã¯ã·ã§ã³ãç¥ãããããšãéèŠã§ãïŒèª¿æ»ããããã¯ã®ééãç§»åã®å®äº-ããã¯ãã¹ãŠè¯ãããšã§ãïŒããããè¡ãããã«ãç¹å¥ãªã¯ã©ã¹ãéçºããŸãã class Action( val type: Type, var isEnabled: Boolean = true, val data: Int = 0 ) { enum class Type { NONE,
å
éšåæType
ã¯ãå®è¡ãããã¢ã¯ã·ã§ã³ã®ã¿ã€ããèšè¿°ããŸãããã®ãã£ãŒã«ãã¯isEnabled
ãéã¢ã¯ãã£ãç¶æ
ã®ã¢ã¯ã·ã§ã³ã衚瀺ããããã«å¿
èŠã§ããã€ãŸãããã®ã¢ã¯ã·ã§ã³ã¯é垞䜿çšå¯èœã§ããããçŸæç¹ã§ã¯äœããã®çç±ã§å®è¡ã§ããªãããšãå ±åããããšã§ãïŒãã®ãããªè¡šç€ºã¯ãã¢ã¯ã·ã§ã³ããŸã£ãã衚瀺ãããªãå Žåãããã¯ããã«æçã§ãïŒãããããã£data
ïŒäžéšã®ã¿ã€ãã®ã¢ã¯ã·ã§ã³ã«å¿
èŠïŒã«ã¯ã远å ã®è©³çްïŒãŠãŒã¶ãŒãéžæããäœçœ®ã®ã€ã³ããã¯ã¹ããªã¹ãããéžæããã¢ã€ãã ã®çªå·ãªã©ïŒãå ±åããç¹å¥ãªå€ãæ ŒçŽãããŸããã¯ã©ã¹Action
ã²ãŒã ãšã³ãžã³ãšå
¥åºåã·ã¹ãã ã®éã®äž»èŠãªãã€ã³ã¿ãŒãã§ãŒã¹ãã§ãïŒä»¥äžã«ã€ããŠïŒãå€ãã®å Žåãããã€ãã®ã¢ã¯ã·ã§ã³ãããã®ã§ïŒããã§ãªããã°ããªãéžæããã®ã§ããïŒïŒããããã¯ã°ã«ãŒãïŒãªã¹ãïŒã«çµåãããŸããæšæºã®ã³ã¬ã¯ã·ã§ã³ã䜿çšãã代ããã«ãç¬èªã®æ¡åŒµã³ã¬ã¯ã·ã§ã³ãäœæããŸãã class ActionList : Iterable<Action> { private val actions = mutableListOf<Action>() val size get() = actions.size fun add(action: Action): ActionList { actions.add(action) return this } fun add(type: Action.Type, enabled: Boolean = true): ActionList { add(Action(type, enabled)) return this } fun addAll(actions: ActionList): ActionList { actions.forEach { add(it) } return this } fun remove(type: Action.Type): ActionList { actions.removeIf { it.type == type } return this } operator fun get(index: Int) = actions[index] operator fun get(type: Action.Type) = actions.find { it.type == type } override fun iterator(): Iterator<Action> = ActionListIterator() private inner class ActionListIterator : Iterator<Action> { private var position = -1 override fun hasNext() = (actions.size > position + 1) override fun next() = actions[++position] } companion object { val EMPTY get() = ActionList() } }
ãã®ã¯ã©ã¹ã«ã¯ããªã¹ãã«ã¢ã¯ã·ã§ã³ã远å ããã³åé€ããããã®ããŸããŸãªã¡ãœããïŒãã§ãŒã³åå¯èœïŒãããã³ã€ã³ããã¯ã¹ãšã¿ã€ãã®äž¡æ¹ãååŸããããã®ããŸããŸãªã¡ãœãããå«ãŸããŸãïŒããªãŒããŒããŒããã«æ³šæããŠãã ããget()
-è§æ¬åŒ§æŒç®åã¯ãªã¹ãã«é©çšã§ããŸãïŒãã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£
ã«ãããIterator
ã¯ã©ã¹ã§ããããçš®é¡ã®çæ°ã®ããããšïŒæ©èœãahaïŒã§ããŸããŸãªã¹ããªãŒã æäœãè¡ãããšãã§ããŸãã空ã®ãªã¹ãããã°ããäœæããããã«ãEMPTYå€ãæäŸãããŸããã¹ã¯ãªãŒã³ãæåŸã«ãçŸåšè¡šç€ºãããŠããããŸããŸãªçš®é¡ã®ã³ã³ãã³ãã説æããå¥ã®ãªã¹ã...ããªãã¯ç§ãèŠãŠç®ãç¬ããŸãããã®ã¯ã©ã¹ãããæç¢ºã«èª¬æããæ¹æ³ãèãå§ãããšããç§ã¯æ¬åœã«äœãçè§£ã§ããªãã£ãã®ã§ãããŒãã«ã«é ãæã¡ãŸãããèªåèªèº«ãçè§£ããç§ã¯é¡ã£ãŠããŸãã enum class GameScreen { HERO_TURN_START, LOCATION_INTERIOR, GAME_LOSS }
äŸã§äœ¿çšãããŠãããã®ã®ã¿ãéžæããŸãããåå¥ã®ã¬ã³ããªã³ã°æ¹æ³ããããã®ããããã«æäŸãããŸã...ç§ã¯åã³äžå¯è§£ã«èª¬æããŸããã衚瀺ãããã³ãå
¥åãããããŠä»ãç§ãã¡ã¯ã€ãã«æãéèŠãªãã€ã³ãã«å°éããŸãã-ã²ãŒã ãšã³ãžã³ãšãŠãŒã¶ãŒïŒãã¬ã€ã€ãŒïŒãšã®çžäºäœçšã§ãããã®ãããªé·ãå°å
¥ã«ãŸã 飜ããŠããªãå Žåã¯ãããã2ã€ã®éšåãæ©èœçã«åé¢ããããšã«åæããããšãããããèŠããŠããã§ãããããããã£ãŠãI / Oã·ã¹ãã ã®ç¹å®ã®å®è£
ã®ä»£ããã«ãã€ã³ã¿ãŒãã§ã€ã¹ã®ã¿ãæäŸããŸããããæ£ç¢ºã«ã¯ã2ãæåã®ã€ã³ã¿ãŒãã§ãŒã¹GameRenderer
ãç»é¢ã«ç»åã衚瀺ããããã«èšèšãããŠããŸããç»é¢ãµã€ãºãç¹å®ã®ã°ã©ãã£ãã¯ã©ã€ãã©ãªãªã©ããæœè±¡åãããŠããããšãæãåºããŠãã ããã ãdraw me thisããšããã³ãã³ããéä¿¡ããã ãã§ããç»é¢ã«ã€ããŠã®äžæçãªäŒè©±ãçè§£ããŠãã人ã¯ããããã®ç»é¢ã®ãããããã€ã³ã¿ãŒãã§ã€ã¹å
ã«ç¬èªã®ã¡ãœãããæã£ãŠããããšããã§ã«æšæž¬ããŠããŸãã interface GameRenderer { fun drawHeroTurnStart(hero: Hero) fun drawLocationInteriorScreen( location: Location, heroesAtLocation: List<Hero>, timer: Int, currentHero: Hero, battleCheck: DieBattleCheck?, encounteredDie: DiePair?, pickedDice: HandMask, activePositions: HandMask, statusMessage: StatusMessage, actions: ActionList ) fun drawGameLoss(message: StatusMessage) }
远å ã®èª¬æã¯å¿
èŠãªããšæããŸã-転éããããã¹ãŠã®ãªããžã§ã¯ãã®ç®çã¯äžèšã§è©³çްã«èª¬æãããŠããŸãããŠãŒã¶ãŒå
¥åã®ããã«ãå¥ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããŸã- GameInteractor
ïŒã¯ããã¹ãã«ãã§ãã¯ã¹ã¯ãªããã¯åžžã«ãã®åèªã匷調ããŸãããããã¯âŠãšæãããŸããïŒãåœŒã®æ¹æ³ã¯ãããŸããŸãªç¶æ³ã§å¿
èŠãªã³ãã³ãããã¬ã€ã€ãŒã«èŠæ±ããŸãïŒææ¡ããããã®ã®ãªã¹ãããã¢ã¯ã·ã§ã³ãéžæãããªã¹ãããèŠçŽ ãéžæããæãããã¥ãŒããéžæããå°ãªããšãäœããæŒãã ããªã©ãå
¥åã¯åæçã§ããïŒã²ãŒã ã¯æ®µéçã§ããïŒããšãã€ãŸãããŠãŒã¶ãŒããªã¯ãšã¹ãã«å¿çãããŸã§ã²ãŒã ãµã€ã¯ã«ã®å®è¡ãäžæãããããšã«æ³šæããŠãã ããã interface GameInteractor{ fun anyInput() fun pickAction(list: ActionList): Action fun pickDiceFromHand(activePositions: HandMask, actions: ActionList): Action }
æåŸã®æ¹æ³ã«ã€ããŠããå°ããååã瀺ãããã«ãfromã¯ããŠãŒã¶ãŒãæãããã¥ãŒããéžæããããã«æåŸ
ãããªããžã§ã¯ãHandMask
-ã¢ã¯ãã£ããªããžã·ã§ã³ã®æ°ãæäŸããŸããã¡ãœããã®å®è¡ã¯ããããã®ããã€ããéžæããããŸã§ç¶ããŸãããã®å Žåãã¡ãœããã¯ããã£ãŒã«ãå
ã®éžæãããäœçœ®ã®çªå·ãšãšãã«ã¿ã€ãHAND_POSITION
ïŒãŸãã¯HAND_ALLY_POSITION
mdaïŒã®ã¢ã¯ã·ã§ã³ãè¿ããŸãdata
ãããã«ããªããžã§ã¯ãããå¥ã®ã¢ã¯ã·ã§ã³ïŒCONFIRM
ãŸãã¯ãªã©CANCEL
ïŒãéžæããããšãã§ããŸãActionList
ãå
¥åã¡ãœããã®å®è£
ã§ã¯ããã£ãŒã«ããisEnabled
èšå®ãããŠããç¶æ³ãåºå¥ãfalse
ããã®ãããªã¢ã¯ã·ã§ã³ã®ãŠãŒã¶ãŒå
¥åãç¡èŠããå¿
èŠããããŸããã²ãŒã ãšã³ãžã³ã¯ã©ã¹ãç§ãã¡ã¯ä»äºã«å¿
èŠãªãã¹ãŠã®è¯ããæãæ¥ãããããŠå®è£
ããããšã³ãžã³ãèæ
®ããŸãããã¯ã©ã¹ãäœæããGame
次ã®ã³ã³ãã³ãïŒç³ãèš³ãããŸããããããã¯å°è±¡çãªäººã«ã¯è¡šç€ºãããŸããã class Game( private val renderer: GameRenderer, private val interactor: GameInteractor, private val scenario: Scenario, private val locations: List<Location>, private val heroes: List<Hero>) { private var timer = 0 private var currentHeroIndex = -1 private lateinit var currentHero: Hero private lateinit var currentLocation: Location private val deterrentPile = Pile() private var encounteredDie: DiePair? = null private var battleCheck: DieBattleCheck? = null private val activeHandPositions = HandMask() private val pickedHandPositions = HandMask() private var phase: GamePhase = GamePhase.SCENARIO_START private var screen = GameScreen.SCENARIO_INTRO private var statusMessage = StatusMessage.EMPTY private var actions: ActionList = ActionList.EMPTY fun start() { if (heroes.isEmpty()) throw IllegalStateException("Heroes list is empty!") if (locations.isEmpty()) throw IllegalStateException("Location list is empty!") heroes.forEach { it.isAlive = true } timer = scenario.initialTimer
ã¡ãœããstart()
-ã²ãŒã ãžã®ãšã³ããªãã€ã³ããããã§ã¯ã倿°ãåæåãããããŒããŒãèšéãããæãç«æ¹äœã§æºããããã¬ããŒã¿ãŒã¯ããããé¢ããã«ã¡ã©ã§èŒããŸããã¡ã€ã³ãµã€ã¯ã«ã¯æ¯åèµ·åããããã®åŸã¯åæ¢ã§ããªããªããŸããã¡ãœããdrawInitialHand()
ã¯ããèªäœãç©èªã£ãŠããŸãïŒdrawOfType()
ã¯ã©ã¹ã¡ãœããã®ã³ãŒããèæ
®ããªãã£ãããã§ãBag
ããäžç·ã«é·ãéã®ããé²ãã åŸããã®ã³ãŒããèªåã§æžãããšãã§ããŸãïŒããã®ã¡ãœããã«refillHeroHand()
ã¯2ã€ã®ãªãã·ã§ã³ïŒåŒæ°ã®å€ã«å¿ããŠredrawScreen
ïŒããããŸãïŒé«éã§éãïŒã²ãŒã ã®éå§æã«ãã¹ãŠã®ããŒããŒã®æãæºããå¿
èŠãããå ŽåïŒãããã³å€§å£°ã§ããã¹ã§ãç§»åã®çµããã«ããã°ãããã¥ãŒããäžå¯§ã«åãé€ããæãé©åãªãµã€ãºã«ããå¿
èŠããããŸããæ¬¡ã§å§ãŸãååã®ã¡ãœããã®æchangePhase
ã-ãã§ã«è¿°ã¹ãããã«ããããã¯çŸåšã®ã²ãŒã ãã§ãŒãºã倿Žãã圹å²ãæãããã²ãŒã 倿°ã®å¯Ÿå¿ããå€ã®å²ãåœãŠã«åŸäºããŠããŸããããã§ã¯actions
ããã®ãã§ãŒãºã«ç¹æã®ã¢ã¯ã·ã§ã³ã远å ããããªã¹ãã圢æãããŸããäžè¬åããã圢åŒã®ãŠãŒãã£ãªãã£ã¡ãœããpickDiceFromHand()
ã¯ãæãããã¥ãŒããéžæããŸããããHandMaskRule
ã§ã¯ãéžæèŠåãå®çŸ©ãã䜿ãæ
£ããã¯ã©ã¹ã®ãªããžã§ã¯ããæž¡ãããŸãããŸããéžæãæåŠããallowCancel
æ©èœïŒïŒãonEachLoop
ããã³éžæãããã¥ãŒãã®ãªã¹ãã倿Žããããã³ã«ã³ãŒããåŒã³åºãå¿
èŠããã颿°ïŒéåžžã¯ç»é¢ã®åæç»ïŒã瀺ããŸãããã®ã¡ãœããã«ãã£ãŠéžæããããã¥ãŒãã¯ãcollectPickedDice()
ããã³ã¡ãœããã䜿çšããŠæã§çµã¿ç«ãŠãããšãã§ããŸãcollectPickedAllyDice()
ãå¥ã®ãŠãŒãã£ãªãã£ã¡ãœããperformStatDieAcquireCheck()
æ°ãããã¥ãŒããååŸããããã®ãã¹ãã«åæ ŒããããŒããŒãå®å
šã«å®è£
ããŸãããã®ã¡ãœããã®äžå¿çãªåœ¹å²ã¯ããªããžã§ã¯ãã«ãã£ãŠæããããŸãDieBattleCheck
ãããã»ã¹ã¯ãã¡ãœããã«ãããã¥ãŒãã®éžæããå§ãŸããŸãpickDiceFromHand()
ïŒåã¹ãããã§ããåå è
ãã®ãªã¹ããæŽæ°ãããŸãDieBattleCheck
ïŒãéžæãããã¥ãŒããæããåé€ããããã®åŸãããŒã«ããçºçããŸã-åãã€ã¯å€ãæŽæ°ãïŒé£ç¶ããŠ8åïŒããã®åŸãçµæãèšç®ãããŠè¡šç€ºãããŸããããŒã«ã«æåãããšãæ°ãããã€ã¹ãããŒããŒã®æã«èœã¡ãŸãããã¹ãã«åå ããŠãããã¥ãŒãã¯ãä¿æãããŠããïŒéã®å ŽåshouldDiscard = true
ïŒããç Žæ£ãããŠããïŒã®å ŽåshouldDiscard = false
ïŒããããã°ã®äžã«é ãããŠããŸãïŒã®å ŽåïŒãäž»ãªæ¹æ³processCycle()
ç¡éã«ãŒãïŒç§ã¯å€±ç¥ããã«å°ããŸãïŒãå«ãŸããŸããæåã«ç»é¢ãæç»ãããæ¬¡ã«ãŠãŒã¶ãŒã«å
¥åãæ±ããããæ¬¡ã«ãã®å
¥åãåŠçãããŸã-ãã®åŸã®ãã¹ãŠã®çµæãã¡ãœããdrawScreen()
ã¯ãGameRenderer
çŸåšã®å€ã«å¿ããŠãç®çã®ã€ã³ã¿ãŒãã§ã€ã¹ã¡ãœãããåŒã³åºããscreen
å¿
èŠãªãªããžã§ã¯ããå
¥åã«æž¡ããŸãããŸãããã®ã¯ã©ã¹ã«ã¯ãããã€ãã®ãã«ããŒã¡ãœãããå«ãŸããŠããŸãcheckLocationCanBeExplored()
ãcheckHeroCanAttemptStatCheck()
ãšcheckHeroCanAcquireDie()
ã圌ãã®ååã¯åœŒãèªèº«ã®ããã«èªã£ãŠããŸãããããã£ãŠãç§ãã¡ã¯ãããã«ã€ããŠè©³ããã¯è¿°ã¹ãŸãããAudio
èµ€ãæ³¢ç·ã§äžç·ãåŒãããã¯ã©ã¹ã¡ãœããåŒã³åºãããããŸãããšããããã³ã¡ã³ãããŠãã ããããã®ç®çã¯åŸã§æ€èšããŸãã誰ãäœããŸã£ããçè§£ããŠããªããããã«å³ã瀺ããŸãïŒããããããããããã«ïŒã 以äžã§ãã²ãŒã ã®æºåã¯å®äºã§ãïŒheheïŒãæ®ãã®äºçްãªäºã¯æ®ããŸããããããã«ã€ããŠã¯ä»¥äžã«ãã¹ããã9ãç»åã衚瀺ãã
ããã§ã仿¥ã®äŒè©±ã®ã¡ã€ã³ãããã¯ãã€ãŸãã¢ããªã±ãŒã·ã§ã³ã®ã°ã©ãã£ãã¯ã³ã³ããŒãã³ãã«ã€ããŠèª¬æããŸãããåç¥ã®ããã«ãç§ãã¡ã®ä»äºã¯ã€ã³ã¿ãŒãã§ã€ã¹GameRenderer
ãšãã®3ã€ã®ã¡ãœãããå®è£
ããããšã§ããããŒã ã«ã¯ãŸã æèœã®ããã¢ãŒãã£ã¹ããããªããããç䌌ã°ã©ãã£ãã¯ã䜿çšããŠç¬èªã«ãããè¡ããŸããããããåå¿è
ã«ãšã£ãŠã¯ãåºå£ã§äžè¬çã«èŠããããã®ãçè§£ããŠãããšããã§ãããããããŠããããæ¬¡ã®å
容ã®3ã€ã®ç»é¢ãèŠãããšæããŸããç»é¢1.ãã¬ã€ã€ãŒã¿ãŒã³ID ç»é¢2.ãšãªã¢ãšçŸåšã®ããŒããŒã«é¢ããæ
å ± ç»é¢3.ã¹ã¯ãªããæå€±ã¡ãã»ãŒãž 衚瀺ãããŠããç»åã¯ãJavaã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ãœãŒã«ã§é垞䜿çšããããã®ãšã¯ç°ãªããã®ã§ãããéåžžã®æ©èœã§prinltn()
ã¯æããã«äžååã§ããããšãã倧倿°ããã§ã«èªèããŠãããšæããŸãããŸããç»é¢äžã®ä»»æã®å Žæã«ãžã£ã³ãããŠãç°ãªãè²ã§ã·ã³ãã«ãæç»ã§ããããã«ããããšæããŸããããããšããŒã«ã®ANSIã³ãŒããç§ãã¡ã®å©ãã«é§ãã€ããŸã ãå¥åŠãªæååãåºåã«éä¿¡ããããšã§ãããã¹ã/èæ¯ã®è²ãæåã®æç»æ¹æ³ãç»é¢äžã®ã«ãŒãœã«ã®äœçœ®ãªã©ã倿Žãããªã©ãå¥åŠãªå¹æãåŸãããšãã§ããŸãããã¡ãããããããçŽç²ãªåœ¢ã§ç޹ä»ããã®ã§ã¯ãªããã¯ã©ã¹ã®ã¡ãœããã®èåŸã«å®è£
ãé ããŸãããããŠãã¯ã©ã¹èªäœããŒãããæžãããšã¯ããŸãã-幞ããªããšã«ãè³¢ã人ã
ãç§ãã¡ã®ããã«ãããããŠãããŸãããJansiãªã©ã®è»œéã©ã€ãã©ãªãããŠã³ããŒãããŠãããžã§ã¯ãã«æ¥ç¶ããã ãã§ãã <dependency> <groupId>org.fusesource.jansi</groupId> <artifactId>jansi</artifactId> <version>1.17.1</version> <scope>compile</scope> </dependency>
ãããŠãäœæãéå§ã§ããŸãããã®ã©ã€ãã©ãªã¯ããã§ãŒã³åã§ãã䟿å©ãªã¡ãœããã®æãæã€ã¯ã©ã¹ãªããžã§ã¯ãAnsi
ïŒéçåŒã³åºãã®çµæãšããŠååŸãããAnsi.ansi()
ïŒãæäŸããŸããããã¯StringBuilder
'a ã®ååã«åºã¥ããŠåäœããŸã-æåã«ãªããžã§ã¯ããäœæãããããå°å·ã«éããŸãã䟿å©ãªã¡ãœããã®ãã¡ã䟿å©ãªãã®ãèŠã€ããŸããa()
-æåã衚瀺ããŸããcursor()
-ç»é¢äžã§ã«ãŒãœã«ãç§»åããŸããeraseLine()
-ãŸãã§èªåèªèº«ã®ããšã話ããã®ããã«ãeraseScreen()
-åæ§ã«;fg(), bg(), fgBright(), bgBright()
-ããã¹ããšèæ¯è²ãæäœããããã®éåžžã«äžäŸ¿ãªæ¹æ³-ç¬èªã®ãããå¿«é©ãªãã®ã«ããŸããreset()
-èšå®ãããè²èšå®ãããªãã«ãŒãªã©ããªã»ããããŸãã
ConsoleRenderer
ç§ãã¡ã®ä»äºã«åœ¹ç«ã€ãããããªããŠãŒãã£ãªãã£ã¡ãœãããæã€ã¯ã©ã¹ãäœæããŸããããæåã®ããŒãžã§ã³ã¯æ¬¡ã®ããã«ãªããŸãã abstract class ConsoleRenderer() { protected lateinit var ansi: Ansi init { AnsiConsole.systemInstall() clearScreen() resetAnsi() } private fun resetAnsi() { ansi = Ansi.ansi() } fun clearScreen() { print(Ansi.ansi().eraseScreen(Ansi.Erase.ALL).cursor(1, 1)) } protected fun render() { print(ansi.toString()) resetAnsi() } }
ãã®ã¡ãœããresetAnsi()
ã¯Ansi
ãå¿
èŠãªã³ãã³ãïŒç§»åãåºåãªã©ïŒã§æºããããæ°ããïŒç©ºã®ïŒãªããžã§ã¯ããäœæããŸããèšå
¥ãå®äºãããšãçæããããªããžã§ã¯ãã¯ã¡ãœããã«ãã£ãŠå°å·ã®ããã«éä¿¡ããrender()
ã倿°ã¯æ°ãããªããžã§ã¯ãã§åæåãããŸãããŸã è€éãªããšã¯ãããŸãããïŒãããããªãããã®ã¯ã©ã¹ãä»ã®äŸ¿å©ãªã¡ãœããã§åãå§ããŸãããµã€ãºããå§ããŸããããã»ãšãã©ã®ç«¯æ«ã®æšæºã³ã³ãœãŒã«ã®ãµã€ãºã¯80x24ã§ãã2ã€ã®å®æ°CONSOLE_WIDTH
ãšã§ãã®äºå®ã«æ³šæãCONSOLE_HEIGHT
ãŸããç§ãã¡ã¯ç¹å®ã®å€ã«å·çããããšã¯ããããã¶ã€ã³ãå¯èœãªéããŽã ç¶ã«ããããšããŸãïŒãŠã§ãã®ããã«ïŒã座æšã®çªå·ä»ãã¯1ããå§ãŸããæåã®åº§æšã¯è¡ã2çªç®ã®åº§æšã¯åã§ããããããã¹ãŠãç¥ã£ãŠããŠãŒãã£ãªãã£ã¡ãœãããèšè¿°ããŸãdrawHorizontalLine()
æå®ãããæååãæå®ãããæåã§åããŸãã protected fun drawHorizontalLine(offsetY: Int, filler: Char) { ansi.cursor(offsetY, 1) (1..CONSOLE_WIDTH).forEach { ansi.a(filler) }
ç¹°ãè¿ãã«ãªããŸãããã³ãã³ããåŒã³åºãa()
ããcursor()
ãããã«ãšãã§ã¯ããå®è¡ãããããããšã¯ãªããAnsi
察å¿ããäžé£ã®ã³ãã³ãããªããžã§ã¯ãã«è¿œå ããã ãã§ãããããã®ã·ãŒã±ã³ã¹ãå°å·çšã«éä¿¡ããããšãã«ã®ã¿ãç»é¢ã«è¡šç€ºãããŸããå€å
žçãªãµã€ã¯ã«ã®äœ¿çšãšã®éfor
ã§æ©èœçãªã¢ãããŒãClosedRange
ãšforEach{}
æ ¹æ¬çãªéãã¯ãããŸãã-åéçºè
ã¯ãããããã䟿å©ã§ãããšå€æããŸãããããããç§ã¯æ°ãããã®ãã¹ãŠãæããŠããç¿ã§ãããå
æ²¢ã®ãããã©ã±ãããæ°ããè¡ã«ã©ãããããŠããããã³ãŒããããã³ã³ãã¯ãã«èŠãããšããçç±ã ãã§ãæ©èœäž»çŸ©ã§ããªããã ãŸãç¶ããŸããåãããšãè¡ãå¥ã®ãŠãŒãã£ãªãã£ã¡ãœãããå®è£
ããŸãdrawBlankLine()
drawHorizontalLine(offsetY, ' ')
ãæ¡åŒµåã®ã¿ãå Žåã«ãã£ãŠã¯ãè¡ãå®å
šã«ç©ºã«ããå¿
èŠã¯ãããŸããããæåãšæåŸã«åçŽç·ãæ®ãå¿
èŠããããŸãïŒãã¬ãŒã ãããã§ãïŒãã³ãŒãã¯æ¬¡ã®ããã«ãªããŸãã protected fun drawBlankLine(offsetY: Int, drawBorders: Boolean = true) { ansi.cursor(offsetY, 1) if (drawBorders) { ansi.a('â') (2 until CONSOLE_WIDTH).forEach { ansi.a(' ') } ansi.a('â') } else { ansi.eraseLine(Ansi.Erase.ALL) } }
ã©ã®ããã«ãç䌌ã°ã©ãã£ãã¯ã¹ãããã¬ãŒã ãæç»ããããšã¯ãããŸãããïŒã·ã³ãã«ã¯ããœãŒã¹ã³ãŒãã«çŽæ¥æ¿å
¥ã§ããŸããAltããŒãæŒããªãããæ°åããŒãããã§æåã³ãŒããå
¥åããŸããæŸã㊠å¿
èŠãªASCIIã³ãŒãã¯ã©ã®ãšã³ã³ãŒãã§ãåãã§ãããããæå°éã®ç޳士ã®ã»ããã§ãããããŠãMinecraftã®ããã«ãå¯èœæ§ã¯ããªãã®æ³ååã®éçã«ãã£ãŠã®ã¿å¶éãããŸãããããŠç»é¢ãµã€ãºã protected fun drawCenteredCaption(offsetY: Int, text: String, color: Color, drawBorders: Boolean = true) { val center = (CONSOLE_WIDTH - text.length) / 2 ansi.cursor(offsetY, 1) ansi.a(if (drawBorders) 'â' else ' ') (2 until center).forEach { ansi.a(' ') } ansi.color(color).a(text).reset() (text.length + center until CONSOLE_WIDTH).forEach { ansi.a(' ') } ansi.a(if (drawBorders) 'â' else ' ') }
è±ã«ã€ããŠå°ã話ããŸãããããã®ã¯ã©ã¹ã«Ansi
ã¯Color
ã8ã€ã®åè²ïŒé»ãéãç·ãã·ã¢ã³ãèµ€ã玫ãé»ãã°ã¬ãŒïŒã®å®æ°ãå«ãŸããŠããŸãããããã¯ãè²ãèå¥ããããã«éåžžã«äžäŸ¿ãªfg()/bg()
æãããŒãžã§ã³ãŸãã¯æããããŒãžã§ã³ã®ã¡ãœããã®å
¥åã«æž¡ãå¿
èŠããããŸãfgBright()/bgBright()
æ¹æ³ã§ã¯ã1ã€ã®å€ã§ã¯äžååã§ã-å°ãªããšã2ã€ïŒè²ãšæããïŒãå¿
èŠã§ãããããã£ãŠã宿°ã®ãªã¹ããšæ¡åŒµã¡ãœãããäœæããŸãïŒãã¥ãŒãã®çš®é¡ãšããŒããŒã®ã¯ã©ã¹ã«è²ãããããã€ã³ãããŸãïŒã protected enum class Color { BLACK, DARK_BLUE, DARK_GREEN, DARK_CYAN, DARK_RED, DARK_MAGENTA, DARK_YELLOW, LIGHT_GRAY, DARK_GRAY, LIGHT_BLUE, LIGHT_GREEN, LIGHT_CYAN, LIGHT_RED, LIGHT_MAGENTA, LIGHT_YELLOW, WHITE } protected fun Ansi.color(color: Color?): Ansi = when (color) { Color.BLACK -> fgBlack() Color.DARK_BLUE -> fgBlue() Color.DARK_GREEN -> fgGreen() Color.DARK_CYAN -> fgCyan() Color.DARK_RED -> fgRed() Color.DARK_MAGENTA -> fgMagenta() Color.DARK_YELLOW -> fgYellow() Color.LIGHT_GRAY -> fg(Ansi.Color.WHITE) Color.DARK_GRAY -> fgBrightBlack() Color.LIGHT_BLUE -> fgBrightBlue() Color.LIGHT_GREEN -> fgBrightGreen() Color.LIGHT_CYAN -> fgBrightCyan() Color.LIGHT_RED -> fgBrightRed() Color.LIGHT_MAGENTA -> fgBrightMagenta() Color.LIGHT_YELLOW -> fgBrightYellow() Color.WHITE -> fgBright(Ansi.Color.WHITE) else -> this } protected fun Ansi.background(color: Color?): Ansi = when (color) { Color.BLACK -> ansi.bg(Ansi.Color.BLACK) Color.DARK_BLUE -> ansi.bg(Ansi.Color.BLUE) Color.DARK_GREEN -> ansi.bgGreen() Color.DARK_CYAN -> ansi.bg(Ansi.Color.CYAN) Color.DARK_RED -> ansi.bgRed() Color.DARK_MAGENTA -> ansi.bgMagenta() Color.DARK_YELLOW -> ansi.bgYellow() Color.LIGHT_GRAY -> ansi.bg(Ansi.Color.WHITE) Color.DARK_GRAY -> ansi.bgBright(Ansi.Color.BLACK) Color.LIGHT_BLUE -> ansi.bgBright(Ansi.Color.BLUE) Color.LIGHT_GREEN -> ansi.bgBrightGreen() Color.LIGHT_CYAN -> ansi.bgBright(Ansi.Color.CYAN) Color.LIGHT_RED -> ansi.bgBrightRed() Color.LIGHT_MAGENTA -> ansi.bgBright(Ansi.Color.MAGENTA) Color.LIGHT_YELLOW -> ansi.bgBrightYellow() Color.WHITE -> ansi.bgBright(Ansi.Color.WHITE) else -> this } protected val dieColors = mapOf( Die.Type.PHYSICAL to Color.LIGHT_BLUE, Die.Type.SOMATIC to Color.LIGHT_GREEN, Die.Type.MENTAL to Color.LIGHT_MAGENTA, Die.Type.VERBAL to Color.LIGHT_YELLOW, Die.Type.DIVINE to Color.LIGHT_CYAN, Die.Type.WOUND to Color.DARK_GRAY, Die.Type.ENEMY to Color.DARK_RED, Die.Type.VILLAIN to Color.LIGHT_RED, Die.Type.OBSTACLE to Color.DARK_YELLOW, Die.Type.ALLY to Color.WHITE ) protected val heroColors = mapOf( Hero.Type.BRAWLER to Color.LIGHT_BLUE, Hero.Type.HUNTER to Color.LIGHT_GREEN )
çŸåšãå©çšå¯èœãª16è²ã®ããããã¯ãåäžã®å®æ°ã«ãã£ãŠäžæã«èå¥ãããŸããããã«ããã€ãã®ãŠãŒãã£ãªãã£ã¡ãœãããèšè¿°ããŸããããã®åã«ãã1ã€ãããã¹ãæååã®å®æ°ãã©ãã«ä¿åããããèããŸãããæåå宿°ã¯ããã¹ãŠ1ãæã«ä¿åãããããã«ãå¥ã
ã®ãã¡ã€ã«ã«åãåºãå¿
èŠããããŸããããã«ãããæåå宿°ã®ä¿å®ã容æã«ãªããŸãããŸããããŒã«ã©ã€ãºã«ãšã£ãŠãéèŠã§ã...ãæåå宿°ãå¥ã®ãã¡ã€ã«ã«ç§»åããå¿
èŠããããŸã...ããã§ãããææ
¢ããŸãããã®çš®ã®ãªãœãŒã¹ãæäœããããã®æšæºJavaã¡ã«ããºã ã¯ãjava.util.ResourceBundle
ãã¡ã€ã«ãæäœãããªããžã§ã¯ãã§ã.properties
ããã®ãããªãã¡ã€ã«ããå§ããŸãã # Game status messages choose_dice_perform_check=Choose dice to perform check: end_of_turn_discard_extra=END OF TURN: Discard extra dice: end_of_turn_discard_optional=END OF TURN: Discard any dice, if needed: choose_action_before_exploration=Choose your action: choose_action_after_exploration=Already explored this turn. Choose what to do now: encounter_physical=Encountered PHYSICAL die. Need to pass respective check or lose this die. encounter_somatic=Encountered SOMATIC die. Need to pass respective check or lose this die. encounter_mental=Encountered MENTAL die. Need to pass respective check or lose this die. encounter_verbal=Encountered VERBAL die. Need to pass respective check or lose this die. encounter_divine=Encountered DIVINE die. Can be acquired automatically (no checks needed): die_acquire_success=You have acquired the die! die_acquire_failure=You have failed to acquire the die. game_loss_out_of_time=You ran out of time # Die types physical=PHYSICAL somatic=SOMATIC mental=MENTAL verbal=VERBAL divine=DIVINE ally=ALLY wound=WOUND enemy=ENEMY villain=VILLAIN obstacle=OBSTACLE # Hero types and descriptions brawler=Brawler hunter=Hunter # Various labels avg=avg bag=Bag bag_size=Bag size class=Class closed=Closed discard=Discard empty=Empty encountered=Encountered fail=Fail hand=Hand heros_turn=%s's turn max=max min=min perform_check=Perform check: pile=Pile received_new_die=Received new die result=Result success=Success sum=sum time=Time total=Total # Action names and descriptions action_confirm_key=ENTER action_confirm_name=Confirm action_cancel_key=ESC action_cancel_name=Cancel action_explore_location_key=E action_explore_location_name=xplore action_finish_turn_key=F action_finish_turn_name=inish action_hide_key=H action_hide_name=ide action_discard_key=D action_discard_name=iscard action_acquire_key=A action_acquire_name=cquire action_leave_key=L action_leave_name=eave action_forfeit_key=F action_forfeit_name=orfeit
åè¡ã«ã¯ãæåã§åºåãããããŒãšå€ã®ãã¢ãå«ãŸããŠããŸã=
ããã¡ã€ã«ã¯ã©ãã«ã§ã眮ãããšãã§ããŸã-äž»ãªããšã¯ããã¡ã€ã«ãžã®ãã¹ãã¯ã©ã¹ãã¹ã®äžéšã§ããããšã§ããã¢ã¯ã·ã§ã³ã®ããã¹ãã¯2ã€ã®éšåã§æ§æãããŠããããšã«æ³šæããŠãã ãããæåã®æåã¯ãç»é¢ã«è¡šç€ºããããšãã«é»è²ã§åŒ·èª¿è¡šç€ºãããã ãã§ãªãããã®ã¢ã¯ã·ã§ã³ãå®è¡ããããã«æŒãå¿
èŠãããããŒã決å®ããŸãããããã£ãŠãããããå¥ã
ã«ä¿åãããšäŸ¿å©ã§ãããã ããç¹å®ã®åœ¢åŒïŒããšãã°ãAndroidã§ã¯æååã®ä¿åæ¹æ³ãç°ãªãïŒããæœè±¡åããæåå宿°ãèªã¿èŸŒãããã®ã€ã³ã¿ãŒãã§ã€ã¹ã«ã€ããŠèª¬æããŸãã interface StringLoader { fun loadString(key: String): String }
ããŒã¯å
¥åã«éä¿¡ãããåºåã¯ç¹å®ã®è¡ã§ããå®è£
ã¯ãã€ã³ã¿ãŒãã§ãŒã¹èªäœãšåããããç°¡åã§ãïŒãã¡ã€ã«ãpathã«æ²¿ã£ãŠãããšä»®å®ããŸãsrc/main/resources/text/strings.properties
ïŒã class PropertiesStringLoader() : StringLoader { private val properties = ResourceBundle.getBundle("text.strings") override fun loadString(key: String) = properties.getString(key) ?: "" }
ããdrawStatusMessage()
ã§ãã²ãŒã ãšã³ãžã³ã®çŸåšã®ç¶æ
ãç»é¢ã«è¡šç€ºããStatusMessage
æ¹æ³drawActionList()
ïŒActionList
ïŒãšäœ¿çšå¯èœãªã¢ã¯ã·ã§ã³ã®ãªã¹ãã衚瀺ããæ¹æ³ïŒïŒãå®è£
ããããšã¯é£ãããããŸãããéã ããæãä»ã®å
¬åŒãªæ¹æ³ãšåæ§ã«ãããããã®ã³ãŒãããããŸããããã®äžéšã¯ãã§ã«èŠãŠããŸã...ã ãããããã«ãã¿ãã¬ããããŸã abstract class ConsoleRenderer(private val strings: StringLoader) { protected lateinit var ansi: Ansi init { AnsiConsole.systemInstall() clearScreen() resetAnsi() } protected fun loadString(key: String) = strings.loadString(key) private fun resetAnsi() { ansi = Ansi.ansi() } fun clearScreen() { print(Ansi.ansi().eraseScreen(Ansi.Erase.ALL).cursor(1, 1)) } protected fun render() { ansi.cursor(CONSOLE_HEIGHT, CONSOLE_WIDTH) System.out.print(ansi.toString()) resetAnsi() } protected fun drawBigNumber(offsetX: Int, offsetY: Int, number: Int): Unit = with(ansi) { var currentX = offsetX cursor(offsetY, currentX) val text = number.toString() text.forEach { when (it) { '0' -> { cursor(offsetY, currentX) a(" âââ ") cursor(offsetY + 1, currentX) a("â â ") cursor(offsetY + 2, currentX) a("â â ") cursor(offsetY + 3, currentX) a("â â ") cursor(offsetY + 4, currentX) a(" âââ ") } '1' -> { cursor(offsetY, currentX) a(" â ") cursor(offsetY + 1, currentX) a(" ââ ") cursor(offsetY + 2, currentX) a("â â ") cursor(offsetY + 3, currentX) a(" â ") cursor(offsetY + 4, currentX) a("âââââ ") } '2' -> { cursor(offsetY, currentX) a(" âââ ") cursor(offsetY + 1, currentX) a("â â ") cursor(offsetY + 2, currentX) a(" â ") cursor(offsetY + 3, currentX) a(" â ") cursor(offsetY + 4, currentX) a("âââââ ") } '3' -> { cursor(offsetY, currentX) a("ââââ ") cursor(offsetY + 1, currentX) a(" â ") cursor(offsetY + 2, currentX) a(" ââ ") cursor(offsetY + 3, currentX) a(" â ") cursor(offsetY + 4, currentX) a("ââââ ") } '4' -> { cursor(offsetY, currentX) a(" â ") cursor(offsetY + 1, currentX) a(" ââ ") cursor(offsetY + 2, currentX) a(" â â ") cursor(offsetY + 3, currentX) a("âââââ ") cursor(offsetY + 4, currentX) a(" â ") } '5' -> { cursor(offsetY, currentX) a("âââââ ") cursor(offsetY + 1, currentX) a("â ") cursor(offsetY + 2, currentX) a("ââââ ") cursor(offsetY + 3, currentX) a(" â ") cursor(offsetY + 4, currentX) a("ââââ ") } '6' -> { cursor(offsetY, currentX) a(" âââ ") cursor(offsetY + 1, currentX) a("â ") cursor(offsetY + 2, currentX) a("ââââ ") cursor(offsetY + 3, currentX) a("â â ") cursor(offsetY + 4, currentX) a(" âââ ") } '7' -> { cursor(offsetY, currentX) a("âââââ ") cursor(offsetY + 1, currentX) a(" â ") cursor(offsetY + 2, currentX) a(" â ") cursor(offsetY + 3, currentX) a(" â ") cursor(offsetY + 4, currentX) a(" â ") } '8' -> { cursor(offsetY, currentX) a(" âââ ") cursor(offsetY + 1, currentX) a("â â ") cursor(offsetY + 2, currentX) a(" âââ ") cursor(offsetY + 3, currentX) a("â â ") cursor(offsetY + 4, currentX) a(" âââ ") } '9' -> { cursor(offsetY, currentX) a(" âââ ") cursor(offsetY + 1, currentX) a("â â ") cursor(offsetY + 2, currentX) a(" ââââ ") cursor(offsetY + 3, currentX) a(" â ") cursor(offsetY + 4, currentX) a(" âââ ") } } currentX += 6 } } protected fun drawHorizontalLine(offsetY: Int, filler: Char) { ansi.cursor(offsetY, 1) (1..CONSOLE_WIDTH).forEach { ansi.a(filler) } } protected fun drawBlankLine(offsetY: Int, drawBorders: Boolean = true) { ansi.cursor(offsetY, 1) if (drawBorders) { ansi.a('â') (2 until CONSOLE_WIDTH).forEach { ansi.a(' ') } ansi.a('â') } else { ansi.eraseLine(Ansi.Erase.ALL) } } protected fun drawCenteredCaption(offsetY: Int, text: String, color: Color, drawBorders: Boolean = true) { val center = (CONSOLE_WIDTH - text.length) / 2 ansi.cursor(offsetY, 1) ansi.a(if (drawBorders) 'â' else ' ') (2 until center).forEach { ansi.a(' ') } ansi.color(color).a(text).reset() (text.length + center until CONSOLE_WIDTH).forEach { ansi.a(' ') } ansi.a(if (drawBorders) 'â' else ' ') } protected fun drawStatusMessage(offsetY: Int, message: StatusMessage, drawBorders: Boolean = true) {
ãªãç§ãã¡å
šå¡ããããããã®ã§ããïŒã¯ãããã®çŽ æŽãããclassããã€ã³ã¿ãŒãã§ãŒã¹å®è£
ãç¶æ¿ããããã«GameRenderer
ãããã¯ãæåã®æãåçŽãªã¡ãœããã®å®è£
ãã©ã®ããã«èŠãããã§ãïŒ override fun drawGameLoss(message: StatusMessage) { val centerY = CONSOLE_HEIGHT / 2 (1 until centerY).forEach { drawBlankLine(it, false) } val data = loadString(message.toString().toLowerCase()).toUpperCase() drawCenteredCaption(centerY, data, LIGHT_RED, false) (centerY + 1..CONSOLE_HEIGHT).forEach { drawBlankLine(it, false) } render() }
è¶
èªç¶çãªãã®ã§ã¯ãªãdata
ãç»é¢ã®äžå€®ã«ããã¹ãè¡ïŒïŒãèµ€ã§1ã€ã ãæç»ããŸãïŒdrawCenteredCaption()
ïŒãã³ãŒãã®æ®ãã¯ãç»é¢ã®æ®ãã空çœè¡ã§åããŸãããããã誰ãããããå¿
èŠãªçç±ãå°ããã§ããã-çµå±ã®ãšããclearScreen()
ãã¡ãœããããããã¡ãœããã®æåã«ãããåŒã³åºããç»é¢ãã¯ãªã¢ããŠãããå¿
èŠãªããã¹ããæç»ããã ãã§ååã§ããæ®å¿µãªãããããã¯ç§ãã¡ã䜿çšããªãæ laãªã¢ãããŒãã§ãããã®çç±ã¯éåžžã«ç°¡åã§ãããã®ã¢ãããŒãã§ã¯ãç»é¢äžã®äžéšã®äœçœ®ã2åæç»ãããç¹ã«ç»é¢ãé£ç¶ããŠæ°åé£ç¶ããŠæç»ãããå ŽåïŒã¢ãã¡ãŒã·ã§ã³äžïŒã«ãã¡ãã€ããé¡èã«ãªããŸãããããã£ãŠãç§ãã¡ã®ã¿ã¹ã¯ã¯ãé©åãªå Žæã«é©åãªãã£ã©ã¯ã¿ãŒãæãã ãã§ãªããå
šäœãåããããšã§ãç©ºã®æåãå«ãç»é¢ã®æ®ãã®éšåïŒä»ã®ã¬ã³ããªã³ã°ããã®ã¢ãŒãã£ãã¡ã¯ããç»é¢ã«æ®ããªãããã«ããããïŒããããŠããã®ã¿ã¹ã¯ã¯ããã»ã©åçŽã§ã¯ãããŸãããæ¬¡ã®æ¹æ³ã¯ããã®ååã«åŸããŸãã override fun drawHeroTurnStart(hero: Hero) { val centerY = (CONSOLE_HEIGHT - 5) / 2 (1 until centerY).forEach { drawBlankLine(it, false) } ansi.color(heroColors[hero.type]) drawHorizontalLine(centerY, 'â') drawHorizontalLine(centerY + 4, 'â') ansi.reset() ansi.cursor(centerY + 1, 1).eraseLine() ansi.cursor(centerY + 3, 1).eraseLine() ansi.cursor(centerY + 2, 1) val text = String.format(loadString("heros_turn"), hero.name.toUpperCase()) val index = text.indexOf(hero.name.toUpperCase()) val center = (CONSOLE_WIDTH - text.length) / 2 ansi.cursor(centerY + 2, center) ansi.eraseLine(Ansi.Erase.BACKWARD) ansi.a(text.substring(0, index)) ansi.color(heroColors[hero.type]).a(hero.name.toUpperCase()).reset() ansi.a(text.substring(index + hero.name.length)) ansi.eraseLine(Ansi.Erase.FORWARD) (centerY + 5..CONSOLE_HEIGHT).forEach { drawBlankLine(it, false) } render() }
ããã§ã¯ãäžå€®æãã®ããã¹ãã«å ããŠã2æ¬ã®æ°Žå¹³ç·ããããŸãïŒäžã®ã¹ã¯ãªãŒã³ã·ã§ãããåç
§ïŒãäžå€®ã®ã¬ã¿ãªã³ã°ã¯2è²ã§è¡šç€ºãããããšã«æ³šæããŠãã ããããŸããåŠæ ¡ã§æ°åŠãåŠã¶ããšã¯ãŸã æçšã§ããããšã確èªããŠãã ãããããŠãæãåçŽãªã¡ãœãããèŠãŠãå®è£
ãç¥ãæãæ¥ãŸããdrawLocationInteriorScreen()
ãããªãèªèº«ãçè§£ããŠããããã«ãããã«ã¯ããã«æ¡éãã®ã³ãŒãããããŸããããã«ãç»é¢ã®ã³ã³ãã³ãã¯ãŠãŒã¶ãŒã®ã¢ã¯ã·ã§ã³ã«å¿ããŠåçã«å€åãããããåžžã«åæç»ããå¿
èŠããããŸãïŒã¢ãã¡ãŒã·ã§ã³ã䜿çšããå ŽåããããŸãïŒãããŠãæçµçã«ããªããçµããããããã«ïŒäžèšã®ã¹ã¯ãªãŒã³ã·ã§ããã«å ããŠããã®ã¡ãœããã®ãã¬ãŒã ã¯ãŒã¯ã§ã¯ãããã«3ã€ã®è¡šç€ºãå®è£
ããå¿
èŠãããããšãæ³åããŠãã ããïŒ1.è¢ãããã¥ãŒããåãåºããŠäŒã 2.ãã¹ãã«åæ Œãããµã€ã³ããéžæãã 3.ãã¹ãçµæã®è¡šç€º ãããã£ãŠãããã«ããªããžã®ç§ã®çŽ æŽãããã¢ããã€ã¹ããããŸãïŒ1ã€ã®ã¡ãœããã«ãã¹ãŠã®ã³ãŒããæŒã蟌ãŸãªãã§ãã ãããå®è£
ãããã€ãã®ã¡ãœããã«åå²ããŸãïŒåã¡ãœããã1åã ãåŒã³åºãããå Žåã§ãïŒãããŠãããŽã ããå¿ããªãã§ãã ããããããããªãã®ç®ã«æ³¢çŽãå§ããããæ°ç§éãŸã°ããããŠãã ãã-ããã¯åœ¹ç«ã€ã¯ãã§ã class ConsoleGameRenderer(loader: StringLoader) : ConsoleRenderer(loader), GameRenderer { private fun drawLocationTopPanel(location: Location, heroesAtLocation: List<Hero>, currentHero: Hero, timer: Int) { val closedString = loadString("closed").toLowerCase() val timeString = loadString("time") val locationName = location.name.toString().toUpperCase() val separatorX1 = locationName.length + if (location.isOpen) { 6 + if (location.bag.size >= 10) 2 else 1 } else { closedString.length + 7 } val separatorX2 = CONSOLE_WIDTH - timeString.length - 6 - if (timer >= 10) 1 else 0
ãã®ãã¹ãŠã®ã³ãŒãã®åäœããã§ãã¯ããããšã«é¢é£ãã1ã€ã®å°ããªåé¡ããããŸããçµã¿èŸŒã¿ã®IDEã³ã³ãœãŒã«ã¯ANSIãšã¹ã±ãŒãã·ãŒã±ã³ã¹ããµããŒãããªããããå€éšç«¯æ«ã§ã¢ããªã±ãŒã·ã§ã³ãèµ·åããå¿
èŠããããŸãïŒä»¥åã«èµ·åããããã®ã¹ã¯ãªãããæ¢ã«äœæããŸããïŒãããã«ãANSIãµããŒãã§ã¯ãWindowsã§ãã¹ãŠãããŸãããããã§ã¯ãããŸãã-ç§ã®ç¥ãéããæšæºã®cmd.exeã§ã®ã¿é«å質ã®è¡šç€ºãå¯èœã«ãªããŸãïŒãããŠãç§ãã¡ãçŠç¹ãåœãŠãªãåé¡ããããŸãïŒããŸããPowerShellã¯ã·ãŒã±ã³ã¹ãèªèããããšãããã«ã¯åŠç¿ããŸããã§ããïŒçŸåšã®èŠæ±ã«ããããããïŒãéãæªãå Žåã¯ãèœèããªãã§ãã ãã-åžžã«ä»£æ¿ãœãªã¥ãŒã·ã§ã³ããããŸãïŒããšãã°ãããïŒããããŠæ¬¡ã«é²ã¿ãŸããã¹ããã10 ãŠãŒã¶ãŒå
¥å
ç»é¢ã«ç»åã衚瀺ããããšã¯ãæŠãã®ååã§ãããŠãŒã¶ãŒããå¶åŸ¡ã³ãã³ããæ£ããåä¿¡ããããšãåæ§ã«éèŠã§ãããŸãããã®ã¿ã¹ã¯ã¯ã以åã®ãã¹ãŠã®ã¿ã¹ã¯ãããå®è£
ãæè¡çã«ã¯ããã«å°é£ã§ããããšã倿ããå¯èœæ§ããããŸãããããããŸãæåã«ã
ããªããæãåºãããã«ãã¯ã©ã¹ã¡ãœãããå®è£
ããå¿
èŠã«çŽé¢ããŠããŸãGameInteractor
ããããã¯3ã€ãããããŸããããç¹å¥ãªæ³šæãå¿
èŠã§ãããŸããåæããã¬ã€ã€ãŒãããŒãæŒããŸã§ãã²ãŒã ãšã³ãžã³ã®äœæ¥ãäžæããå¿
èŠããããŸãã第äºã«ãã¯ãªãã¯åŠçãæ®å¿µãªãããæšæºã¯ã©ã¹ã®å®¹éã¯Reader
ãScanner
ãConsole
ãããã®ã»ãšãã©ã®æŒããèªèããããã«ååã§ã¯ãããŸãããç§ãã¡ã¯ãåã³ãã³ãã®åŸã«EnterããŒãæŒãã«ãŠãŒã¶ãå¿
èŠãšããŸãããKeyListener
'aã®ãããªãã®ãå¿
èŠã§ãããããã¯Swingãã¬ãŒã ã¯ãŒã¯ã«ãã£ãããšæ¥ç¶ãããŠãããã³ã³ãœãŒã«ã¢ããªã±ãŒã·ã§ã³ã«ã¯ãã®ã°ã©ãã£ãã¯èŠæãåãã¯ãããŸãããã©ãããïŒ
ãã¡ãããã©ã€ãã©ãªã®æ€çŽ¢ã¯ãä»åã¯å®å
šã«ãã€ãã£ãã³ãŒãã«äŸåããŸãã ãããããªããã¯ãã¹ãã©ãããã©ãŒã ããšã¯ã©ãããæå³ã§ããïŒæ®å¿µãªããã軜éã§ãã©ãããã©ãŒã ã«äŸåããªã圢åŒã§ã·ã³ãã«ãªæ©èœãå®è£
ããã©ã€ãã©ãªããŸã èŠã€ããŠããŸããããããŸã§ã®éãã¢ã³ã¹ã¿ãŒjLineã«æ³šç®ããŸããããjLineã¯ãã³ã³ãœãŒã«ã§é«åºŠãªãŠãŒã¶ãŒã€ã³ã¿ãŒãã§ã€ã¹ãæ§ç¯ããããã®ããŒãã¹ã¿ãŒãå®è£
ããŠããŸããã¯ãããã€ãã£ãå®è£
ã§ããã¯ããWindowsãšLinux / UNIXã®äž¡æ¹ããµããŒãããŸãïŒé©åãªã©ã€ãã©ãªãæäŸããããšã«ããïŒããããŠãã¯ãã䜿çšäžã®ãã®æ©èœã®ã»ãšãã©ã¯ãæã
ã¯çŸå¹Žã¯å¿
èŠãããŸãããå¿
èŠãªã®ã¯ãå°ããªãäžååã«ææžåãããæ©äŒã ãã§ããããã®äœæ¥ãããããåæããŸãã <dependency> <groupId>jline</groupId> <artifactId>jline</artifactId> <version>2.14.6</version> <scope>compile</scope> </dependency>
3çªç®ã®ææ°ããŒãžã§ã³ã§ã¯ãªããConsoleReader
methodãæã€ã¯ã©ã¹ããã2çªç®ã®ããŒãžã§ã³ãå¿
èŠã§ããããšã«æ³šæããŠãã ããreadCharacter()
ãååã瀺ãããã«ããã®ã¡ãœããã¯ããŒããŒãã§æŒãããæåã®ã³ãŒããè¿ããŸãïŒåæã«åäœããŸããããããå¿
èŠã§ãïŒãæ®ãã¯æè¡çãªåé¡ã§ããã·ã³ãã«ãšã¢ã¯ã·ã§ã³ã®ã¿ã€ãéã®å¯Ÿå¿è¡šïŒAction.Type
ïŒãäœæããäžæ¹ãã¯ãªãã¯ããŠä»æ¹ãè¿ããŸãããããŒããŒãã®ãã¹ãŠã®ããŒã1æåã§è¡šçŸã§ããããã§ã¯ãªãããšããåç¥ã§ããïŒå€ãã®ããŒã¯ã2ã3ã4æåã®ãšã¹ã±ãŒãã·ãŒã±ã³ã¹ã䜿çšããŸãã圌ããšäžç·ã«ããæ¹æ³ã¯ïŒããæå以å€ã®ããŒãïŒç¢å°ãFããŒãããŒã ãæ¿å
¥ãPgUp / Dnãçµäºãåé€ããã³ããŒãªã©ïŒãèªèãããå Žåãå
¥åã¿ã¹ã¯ã¯è€éã«ãªãããšã«æ³šæããŠãã ãããããããç§ãã¡ã¯æãã§ããªãã®ã§ãç¶ç¶ããŸããConsoleInteractor
å¿
èŠãªãµãŒãã¹ã¡ãœãããæã€ã¯ã©ã¹ãäœæããŸãããã abstract class ConsoleInteractor { private val reader = ConsoleReader() private val mapper = mapOf( CONFIRM to 13.toChar(), CANCEL to 27.toChar(), EXPLORE_LOCATION to 'e', FINISH_TURN to 'f', ACQUIRE to 'a', LEAVE to 'l', FORFEIT to 'f', HIDE to 'h', DISCARD to 'd', ) protected fun read() = reader.readCharacter().toChar() protected open fun getIndexForKey(key: Char) = "1234567890abcdefghijklmnopqrstuvw".indexOf(key) }
ãããmapper
ãšã¡ãœãããèšå®ããŸãread()
ãããã«getIndexForKey()
ããªã¹ãããã¢ã€ãã ãéžæããå¿
èŠãããå Žåããæãããã¥ãŒããéžæããå¿
èŠãããå Žåã«äœ¿çšããæ¹æ³ãæäŸããŸãããã®ã¯ã©ã¹ããã€ã³ã¿ãŒãã§ãŒã¹å®è£
ãç¶æ¿ããããšã¯æ®ã£ãŠããŸãGameInteractor
ããããŠãå®éã«ã¯ãã³ãŒãïŒ class ConsoleGameInteractor : ConsoleInteractor(), GameInteractor { override fun anyInput() { read() } override fun pickAction(list: ActionList): Action { while (true) { val key = read() list .filter(Action::isEnabled) .find { mapper[it.type] == key } ?.let { return it } } } override fun pickDiceFromHand(activePositions: HandMask, actions: ActionList) : Action { while (true) { val key = read() actions.forEach { if (mapper[it.type] == key && it.isEnabled) return it } when (key) { in '1'..'9' -> { val index = key - '1' if (activePositions.checkPosition(index)) { return Action(HAND_POSITION, data = index) } } '0' -> { if (activePositions.checkPosition(9)) { return Action(HAND_POSITION, data = 9) } } in 'a'..'f' -> { val allyIndex = key - 'a' if (activePositions.checkAllyPosition(allyIndex)) { return Action(HAND_ALLY_POSITION, data = allyIndex) } } } } } }
ã¡ãœããã®å®è£
ã¯ãããŸããŸãªäžé©åãªãã³ã»ã³ã¹ãåºããªãããã«ãéåžžã«äžå¯§ã§ç€Œåæ£ãããã®ã§ããéžæããã¢ã¯ã·ã§ã³ãã¢ã¯ãã£ãã§ãããéžæããæã®äœçœ®ã蚱容ã»ããã«å«ãŸããŠããããšã確èªããŸãããããŠãç§ãã¡å
šå¡ãç§ãã¡ã®åšãã®äººã
ã«å¯ŸããŠäžå¯§ã§ããããšãé¡ã£ãŠããŸããã¹ããã11ãé³ãšé³æ¥œ
ãããããããªãã§ã¯ã©ãã§ããããïŒãµãŠã³ãããªãã«ããŠã²ãŒã ããã¬ã€ããããšãããå ŽåïŒããšãã°ãèªå®
ã«èª°ãããªããšãã«ã¿ãã¬ãããã«ããŒã®äžã«çœ®ããå ŽåïŒãã©ãã ãè² ããŠãããã宿ã§ãããããããŸãããã²ãŒã ã®ååã ãããã¬ã€ãããããªãã®ã§ããå€ãã®ã²ãŒã ã¯ãé³ã®äŒŽå¥ãªãã§ã¯æ³åã§ããŸãããå€ãã®å Žåãããã¯äžå¯éçãªèŠä»¶ã§ãããéã®ç¶æ³ããããŸãïŒããšãã°ãååãšããŠé³ããªãå ŽåããŸãã¯ãããããªããã°æ²æšã§ããããããããªããã°è¯ãã§ãããïŒãè¯ãä»äºãããããšã¯ãå®éã«ã¯äžèŠããã»ã©ç°¡åã§ã¯ãããŸããïŒçç±ã¯å€§èŠæš¡ãªã¹ã¿ãžãªã§é«åºŠãªå°éå®¶ããããè¡ãããã§ãïŒããããã¯ãã»ãšãã©ã®å Žåãã²ãŒã å
ã«ãªãŒãã£ãªã³ã³ããŒãã³ãïŒå°ãªããšãããã€ãïŒãæã£ãŠããæ¹ãã¯ããã«è¯ãããšã§ã圌女ãå
šãããªãããæåŸã®ææ®µãšããŠãåŸã§é³è³ªãæ¹åã§ããŸãããæéãšæ°åãèš±ããšãããã®ãžã£ã³ã«ã®ç¹æ§ã«ãããç§ãã¡ã®ã²ãŒã ã¯åäœã®ãµãŠã³ããšãã§ã¯ãã«ãã£ãŠç¹åŸŽä»ããããããšã¯ãããŸãããããŒãã²ãŒã ã®ããžã¿ã«çããã¬ã€ããå Žåããã®æå³ã¯çè§£ã§ããŸããé³ã¯ãã®åäžæ§ãã¯ãããããã«éå±ã«ãªãããã°ãããããšãé³ãªãã§æŒå¥ããããšã¯æ·±å»ãªæå€±ã®ããã«ã¯èŠããªããªããŸããåé¡ã¯ããã®çŸè±¡ã«å¯ŸåŠãã广çãªæ¹æ³ããªããšããäºå®ã«ãã£ãŠæªåããŸããã²ãŒã ãµãŠã³ããå®å
šã«ç°ãªããã®ã«çœ®ãæãããšãæéãçµã€ãšããããããŸããè¯ãã²ãŒã ã§ã¯ããµãŠã³ãã¯ã²ãŒã ãã¬ã€ãè£å®ããé²è¡äžã®ã¢ã¯ã·ã§ã³ã®é°å²æ°ãæããã«ããçãçããšãããã®ã«ããŸã-é°å²æ°ããã ã®ãŽãè¢ã®ããããŒãã«ã§ãããã²ãŒã ãã¬ã€å
šäœããµã€ã³ããæããããšã§æ§æãããŠããå Žåããããéæããããšã¯å°é£ã§ããããã«ãããããããããã¯ãŸãã«ç§ãã¡ãçºèšãããã®ã§ããã·ã«ã¯ã¯ããã«ããããã£ã¹ãã¯ããã«ãããããããã倧ããªå«ã³å£°ã«ãããããŸãããŸãã§ã¹ã¯ãªãŒã³äžã®ç»åã芳å¯ããŠããªããã®ããã«ãå®éã®ç©ççãªãªããžã§ã¯ããšå®éã«ããåãããŠããããã«ããããã¯å®å
šã«ããããæ§ããã«çºå£°ããå¿
èŠããããŸã-ã¹ã¯ãªããå
šäœã§åã100åèãããã®ã§ãé³ãåé¢ã«åºãŠã¯ãªããŸãã-ã²ãŒã ãã¬ã€ãç©ããã«ã·ã§ãŒãã£ã³ã°ããã ãã§ãããããæèœã«éæããæ¹æ³ã¯ïŒããããªããé³ãç¹å¥ãããªããç®ç«ã€æ¬ é¥ã«æ°ã¥ãã磚ãããã«ãã§ããã ãã²ãŒã ããã¬ã€ããããšããå§ãããŸãïŒãã®ã¢ããã€ã¹ã¯ãé³ã ãã§ãªãé©çšãããŸãïŒããããæèœã«éæããæ¹æ³ã¯ïŒããããªããé³ãç¹å¥ãããªããç®ç«ã€æ¬ é¥ã«æ°ã¥ãã磚ãããã«ãã§ããã ãã²ãŒã ããã¬ã€ããããšããå§ãããŸãïŒãã®ã¢ããã€ã¹ã¯ãé³ã ãã§ãªãé©çšãããŸãïŒããããæèœã«éæããæ¹æ³ã¯ïŒããããªããé³ãç¹å¥ãããªããç®ç«ã€æ¬ é¥ã«æ°ã¥ãã磚ãããã«ãã§ããã ãã²ãŒã ããã¬ã€ããããšããå§ãããŸãïŒãã®ã¢ããã€ã¹ã¯ãé³ã ãã§ãªãé©çšãããŸãïŒãçè«ã§ããããæŽçãããããç·Žç¿ã«ç§»ãæãæ¥ãããã§ãããããŠãã®åã«ã質åãããå¿
èŠããããŸããå®éãã²ãŒã ãã¡ã€ã«ãã©ãã§ååŸããã®ã§ãããããæãç°¡åã§ç¢ºå®ãªæ¹æ³-å€ããã€ã¯ã䜿çšããŠããŸãã¯é»è©±ã䜿çšããŠããèŠèŠããå質ã§èªåã§é²é³ããããšãã§ããŸããã€ã³ã¿ãŒãããã«ã¯ããã€ãããã«ã®äžéšã®ãããç·©ããããããŒãã§æ°·ãç ãããããŠã骚ãç ããŠã«ãªã«ãªãšããè骚ã®å¹æãå®çŸããæ¹æ³ã«é¢ãããããªããããããããŸããã·ã¥ã«ã¬ã¢ãªã¹ã ã®çŸåŠã«éŠŽæã¿ã®ãªã人ã¯ãèªåã®å£°ãå°æçšåãæ¥œåšãšããŠäœ¿ãããšãã§ããŸãïŒãããè¡ãããäŸãæåäŸãããããŸãïŒããŸãã¯ãfreesound.orgã«ã¢ã¯ã»ã¹ã§ããŸããã£ãšåã«ä»ã®çŸäººãããªãã®ããã«ãããããå Žæãã©ã€ã»ã³ã¹ã®ã¿ã«æ³šæããŠãã ããïŒå€ãã®èè
ã¯ã倧ããªå³ãåºã«æããããã³ã€ã³ã®ãªãŒãã£ãªé²é³ã«éåžžã«ææã§ã-ããªãã¯æ±ºããŠãå
ã®äœæè
ã«ãéãæã£ããã圌ã®åµé çãªä»®åã«èšåããªãã§åœŒãã®åŽåã®ææãæªçšããããšã¯æ±ºããŠããŸããïŒæã«ã¯éåžžã«å¥åŠã§ãïŒã³ã¡ã³ãã§ã奜ããªãã¡ã€ã«ããã©ãã°ã¢ã³ãããããããŠãã¯ã©ã¹ãã¹ã®ã©ããã«çœ®ããŸããããããèå¥ããããã«ãåã€ã³ã¹ã¿ã³ã¹ã1ã€ã®ãµãŠã³ããšãã§ã¯ãã«å¯Ÿå¿ããåæã䜿çšããŸãã enum class Sound { TURN_START,
ãµãŠã³ãã®åçæ¹æ³ã¯ããŒããŠã§ã¢ãã©ãããã©ãŒã ã«ãã£ãŠç°ãªããããã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããŠç¹å®ã®å®è£
ããæœè±¡åã§ããŸããããšãã°ãããã¯ïŒ interface SoundPlayer { fun play(sound: Sound) }
åè¿°ã®ã€ã³ã¿ãŒãã§ã€ã¹GameRenderer
ããã³ãšåæ§ã«GameInteractor
ããã®å®è£
ãã¯ã©ã¹ã€ã³ã¹ã¿ã³ã¹ãžã®å
¥åã«æž¡ãå¿
èŠããããŸãGame
ããŸããå®è£
ã¯æ¬¡ã®ããã«ãªããŸãã class MuteSoundPlayer : SoundPlayer { override fun play(sound: Sound) {
ãã®åŸãããè峿·±ãå®è£
ãæ€èšããŸãããä»ã¯é³æ¥œã«ã€ããŠè©±ããŸãããã广é³ãšåæ§ã«ãã²ãŒã ã®é°å²æ°ãäœãåºãäžã§å€§ããªåœ¹å²ãæãããŸããåæ§ã«ãåªããã²ãŒã ã¯äžé©åãªé³æ¥œã«ãã£ãŠå°ç¡ãã«ãããå¯èœæ§ããããŸããé³ãšåæ§ã«ã鳿¥œã¯æ§ããã§ãèžè¡çãªå¹æãå¿
èŠãªå Žåãé€ããŠãç»é¢äžã®ã¢ã¯ã·ã§ã³ã«é©åã«å¯Ÿå¿ããå¿
èŠããããŸãïŒåŸ
ã¡äŒããã容赊ãªã殺ãããã¡ã€ã³ã®éåœã誰ããçå£ã«å¹ã蟌ãã ããšãæåŸ
ããªãã§ãã ããïŒããŒããŒãåœŒã®æ²åçãªæ»ã®å Žé¢ãåäŸã®æããã®æ¥œããå°ããªé³æ¥œã䌎ãå ŽåïŒããããéæããã®ã¯éåžžã«é£ãããç¹å¥ã«èšç·Žããã人ã
ããã®ãããªåé¡ã«å¯ŸåŠããŸãïŒç§ãã¡ã¯ãããã«äžæ
£ãã§ãïŒããã²ãŒã æ§ç¯ã®å€©æã®åå¿è
ãšããŠãç§ãã¡ãäœããããããšãã§ããŸããããšãã°ãã©ããã«è¡ãfreemusicarchive.orgãŸãã¯soundcloud.comïŒãYouTubeïŒãšèªå奜ã¿ã«äœããèŠã€ããŸãããã¹ã¯ãããã®å Žåãã¢ã³ããšã³ãã¯è¯ãéžæã§ããã¯ã£ããããã¡ããã£ãŒã®ãªãéãã§æ»ãããªé³æ¥œã§ãèæ¯ã®äœæã«é©ããŠããŸããã©ã€ã»ã³ã¹ã«æ³šæãæã£ãŠãã ãããç¡æã®é³æ¥œã§ãããééçãªå ±é
¬ã§ã¯ãªãã«ããŠããå°ãªããšãæ®éçãªèªèã«å€ããæèœã®ããäœæ²å®¶ã«ãã£ãŠæžãããŠããããšããããŸãããã1ã€åæãäœæããŸãããã enum class Music { SCENARIO_MUSIC_1, SCENARIO_MUSIC_2, SCENARIO_MUSIC_3, }
åæ§ã«ãã€ã³ã¿ãŒãã§ãŒã¹ãšãã®ããã©ã«ãã®å®è£
ãå®çŸ©ããŸãã interface MusicPlayer { fun play(music: Music) fun stop() } class MuteMusicPlayer : MusicPlayer { override fun play(music: Music) {
ãã®å Žåãåçãéå§ããæ¹æ³ãšåæ¢ããæ¹æ³ã®2ã€ãå¿
èŠã§ãããŸãã远å ã®æ¹æ³ïŒäžæåæ¢/åéãå·»ãæ»ããªã©ïŒãå°æ¥äŸ¿å©ã«ãªãå¯èœæ§ãååã«ãããŸããããããŸã§ã®ãšããããã®2ã€ã§ååã§ããæ¯åãªããžã§ã¯ãéã§ãã¬ãŒã€ãŒã¯ã©ã¹ãžã®åç
§ãæž¡ãããšã¯ãéåžžã«äŸ¿å©ãªè§£æ±ºçã§ã¯ãªãããã§ããç§ã¯å¥ã®ãªããžã§ã¯ãã«ãµãŠã³ãã鳿¥œã¡ãœãããæŒå¥ããããã«ããããã«å¿
èŠãªãã¹ãŠã®äœãããšãææ¡ãããã³ãã£ãŒäŒæ¥ã§ãã®ã§ã1æéã§ãæã
ã¯ãäžã€ã ãekzepmlyarãã¬ãŒã€ãŒãå¿
èŠäžå¹çŒïŒã·ã³ã°ã«ãã³ïŒããããã£ãŠãåãã€ã³ã¹ã¿ã³ã¹ãžã®ãªã³ã¯ãçµ¶ããéä¿¡ããã«ãã¢ããªã±ãŒã·ã§ã³å
ã®ã©ãããã§ã責任ãããªãŒãã£ãªãµãã·ã¹ãã ãåžžã«å©çšã§ããŸããæ¬¡ã®ããã«ãªããŸãããªãŒãã£ãªåçã·ã¹ãã ã®ã¯ã©ã¹å³ ã¯ã©ã¹Audio
ã¯ç§ãã¡ã®ã·ã³ã°ã«ãã³ã§ãããµãã·ã¹ãã ã«åäžã®ãã¡ãµãŒããæäŸããŸã...ãšããã§ãããã«ãã¡ãµãŒãïŒãã¡ãµãŒãïŒããããŸããããã¯ããããã®ã€ã³ã¿ãŒãããäžã§åŸ¹åºçã«èšèšãããç¹°ãè¿ãïŒäŸãšãšãã«ïŒèšè¿°ãããå¥ã®ãã¶ã€ã³ãã¿ãŒã³ã§ãããããã£ãŠããã§ã«åŸåããäžæºã®å«ã³å£°ãèããã®ã§ãç§ã¯é·ãéç¥ãããŠããããšã®èª¬æããããŠãå
ã«é²ã¿ãŸããã³ãŒãã¯æ¬¡ã®ãšããã§ãã object Audio { private var soundPlayer: SoundPlayer = MuteSoundPlayer() private var musicPlayer: MusicPlayer = MuteMusicPlayer() fun init(soundPlayer: SoundPlayer, musicPlayer: MusicPlayer) { this.soundPlayer = soundPlayer this.musicPlayer = musicPlayer } fun playSound(sound: Sound) = this.soundPlayer.play(sound) fun playMusic(music: Music) = this.musicPlayer.play(music) fun stopMusic() = this.musicPlayer.stop() }
init()
å¿
èŠãªãªããžã§ã¯ãã§åæåããããšã«ãããæåã®ã©ããã§äžåºŠã ãåŒã³åºãã ãã§ååã§ãããå°æ¥çã«ã¯å®è£
ã®è©³çްãå®å
šã«å¿ããŠäŸ¿å©ãªã¡ãœããã䜿çšããŸããããªããããããªããŠããå¿é
ããªãã§ãã ãããã·ã¹ãã ã¯æ»ã¬ã§ããã-ãªããžã§ã¯ãã¯ããã©ã«ãã®ã¯ã©ã¹ã«ãã£ãŠåæåãããŸãã以äžã§ãã
å®éã®åçãåŠçããããã«æ®ããŸãããµãŠã³ãïŒãŸãã¯ãè³¢ã人ãèšãããã«ããµã³ãã«ïŒã®åçã«é¢ããŠã¯ãJavaã«ã¯äŸ¿å©ãªã¯ã©ã¹AudioSystem
ãšã€ã³ã¿ãŒãã§ãŒã¹ããããŸãClip
ãå¿
èŠãªã®ã¯ããªãŒãã£ãªãã¡ã€ã«ãžã®ãã¹ãæ£ããèšå®ããããšã§ãïŒã¯ã©ã¹ãã¹ã«ãããŸããèŠããŠããŸããïŒïŒã import javax.sound.sampled.AudioSystem class BasicSoundPlayer : SoundPlayer { private fun pathToFile(sound: Sound) = "/sound/${sound.toString().toLowerCase()}.wav" override fun play(sound: Sound) { val url = javaClass.getResource(pathToFile(sound)) val audioIn = AudioSystem.getAudioInputStream(url) val clip = AudioSystem.getClip() clip.open(audioIn) clip.start() } }
ã¡ãœããopen()
ã¯ãããæšãŠãããšãã§ããŸãIOException
ïŒç¹ã«ããã¡ã€ã«åœ¢åŒãæ°ã«å
¥ããªãã£ãå Žå-ãã®å ŽåããªãŒãã£ãªãšãã£ã¿ãŒã§ãã¡ã€ã«ãéããŠåä¿åããããšããå§ãããŸãïŒããã®ããããããã¯ã«ã©ããããããšããå§ãããŸãtry-catch
ããæåã¯ãé³ã«åé¡ããããã³ã«ã¯ã©ãã·ã¥ããŸããããäœãŠèšãã°ããã®ãããããããªã...ã鳿¥œã§ã¯äºæ
ã¯ããã«æªåããŸããç§ã®ç¥ãéããJavaã§é³æ¥œãã¡ã€ã«ïŒmp3圢åŒãªã©ïŒãåçããæšæºçãªæ¹æ³ã¯ãªãããããããã«ããŠããµãŒãããŒãã£ã®ã©ã€ãã©ãªã䜿çšããå¿
èŠããããŸãïŒããŸããŸãªã©ã€ãã©ãªããããŸãïŒãããªã人æ°ã®ããJLayerãªã©ãæå°éã®æ©èœãåãã軜éã®ãã®ãé©ããŠããŸããäŸåããŠè¿œå ããŸãïŒ <dependencies> <dependency> <groupId>com.googlecode.soundlibs</groupId> <artifactId>jlayer</artifactId> <version>1.0.1.4</version> <scope>compile</scope> </dependency> </dependencies>
ãããŠããã¬ãŒã€ãŒã®å©ããåããŠå®è£
ããŸãã class BasicMusicPlayer : MusicPlayer { private var currentMusic: Music? = null private var thread: PlayerThread? = null private fun pathToFile(music: Music) = "/music/${music.toString().toLowerCase()}.mp3" override fun play(music: Music) { if (currentMusic == music) { return } currentMusic = music thread?.finish() Thread.yield() thread = PlayerThread(pathToFile(music)) thread?.start() } override fun stop() { currentMusic = null thread?.finish() }
ãŸãããã®ã©ã€ãã©ãªã¯åæçã«åçãå®è¡ãããã¡ã€ã«ã®çµããã«éãããŸã§ã¡ã€ã³ã¹ããªãŒã ããããã¯ããŸãããããã£ãŠãåå¥ã®ã¹ã¬ããïŒPlayerThread
ïŒãå®è£
ãããããããªãã·ã§ã³ãïŒããŒã¢ã³ïŒã«ããªããã°ãªããŸãããããããããšã§ãã¢ããªã±ãŒã·ã§ã³ãæ©æã«çµäºããããšã¯ãããŸãããæ¬¡ã«ãçŸåšåçäžã®é³æ¥œãã¡ã€ã«ã®èå¥åïŒcurrentMusic
ïŒããã¬ãŒã€ãŒã³ãŒãã«ä¿åãããŸãã 2çªç®ã®ã³ãã³ããçªç¶åçãããå Žåãæåããåçãéå§ããŸããã第äžã«ã鳿¥œãã¡ã€ã«ã®æåŸã«å°éãããšããã®åçãåã³éå§ãããŸã-ã¹ããªãŒã ãã³ãã³ãã«ãã£ãŠæç€ºçã«åæ¢ããããŸã§ç¶ããŸãfinish()
ïŒãŸãã¯æ¢ã«è¿°ã¹ãããã«ãä»ã®ã¹ã¬ãããå®äºãããŸã§ïŒã第4ã«ãäžèšã®ã³ãŒãã¯äžèŠäžå¿
èŠãªãã©ã°ãšã³ãã³ãã§ãã£ã±ãã§ããã培åºçã«ãããã°ããã³ãã¹ããããŠããŸã-ãã¬ã€ã€ãŒã¯æåŸ
ã©ããã«åäœããã·ã¹ãã ãé
ããããéäžã§çªç¶äžæãããã¡ã¢ãªãªãŒã¯ãåŒãèµ·ããããéºäŒåçµã¿æããªããžã§ã¯ããå«ãŸããèŒããŸã鮮床ãšçŽåºŠããããåãããããžã§ã¯ãã§å€§èã«äœ¿çšããŠãã ãããã¹ããã12ãããŒã«ãªãŒãŒã·ã§ã³
ç§ãã¡ã®ã²ãŒã ã¯ã»ãšãã©æºåãã§ããŠããŸããã誰ãããããã¬ã€ããŸããã ãªãã§ïŒ
ããã·ã¢èªã¯ãããŸããïŒ..ãã·ã¢èªã¯ãããŸããïŒ..ãã·ã¢èªã远å ããŠãã ããïŒ..ç¬ãéçºããŠãã ããïŒãåºèã®ãŠã§ããµã€ãã§é¢çœãã¹ããŒãªãŒã²ãŒã ïŒç¹ã«ã¢ãã€ã«ïŒã®ããŒãžãéããã¬ãã¥ãŒãèªãã§ãã ããã圌ãã¯çŽ æŽããããææãã®ã°ã©ãã£ãã¯ãè³è³ãå§ããŸããïŒãŸãã¯å€§æ°ã®ãµãŠã³ããã©ãã¯ã«é©åããŸããïŒãŸãã¯ãæåã®1åéããäžæ¯æ§ããããæåŸãŸã§ææŸããªããšããµã€ãã£ã³ã°ãªã¹ããŒãªãŒã話ããŸããïŒãã
äžæºãªããã¬ã€ã€ãŒãã¯å€ãã®ãŠããããæç€ºããéåžžã¯ã²ãŒã ãåé€ããŸãããããŠã圌ãã¯ãŸãè¿éãå¿
èŠãšããŸã-ãããŠãããããã¹ãŠã¯äžã€ã®ç°¡åãªçç±ã®ããã«ãã¯ããããªãã¯åäœã95ã®ãã¹ãŠã®èšèªã«ç¿»èš³ããã®ãå¿ããŠããŸããããããããã£ãªã¢ãæã倧声ã§å«ã¶äººãããã ãã§ãïŒ
åãããŸããïŒæ°ã¶æã®ããŒãã¯ãŒã¯ãé·ãç ãã¬å€ãçµ¶ãéãªãç¥çµè¡°åŒ±-ããã¯ãã¹ãŠã尻尟ã®äžã®ãã ã¹ã¿ãŒã§ããèšå€§ãªæ°ã®ãã¬ã€ã€ãŒã倱ããŸããããããã¯ä¿®æ£ã§ããŸãããã ããå
ã«èããŠãã ããã察象èªè
ãæ±ºå®ããããã€ãã®äž»èŠèšèªãéžæãã翻蚳ãµãŒãã¹ã泚æããŸã...äžè¬çã«ãä»ã®äººãããŒãèšäºã§è€æ°å説æããããšããã¹ãŠè¡ããŸãïŒç§ãããè³¢ãïŒãåé¡ã®æè¡çãªåŽé¢ã«çŠç¹ãåœãŠã補åãå®å
šã«ããŒã«ã©ã€ãºããæ¹æ³ã«ã€ããŠèª¬æããŸãããŸãããã³ãã¬ãŒãã«å
¥ããŸããååãšèª¬æãåçŽãªãã®ãšããŠä¿åãããåã«èŠããŠãString
ãŸããïŒä»ã§ã¯æ©èœããŸãããããã©ã«ãã®èšèªã«å ããŠããµããŒãããäºå®ã®ãã¹ãŠã®èšèªãžã®ç¿»èš³ãæäŸããå¿
èŠããããŸããããšãã°ã次ã®ããã«ïŒ
class TestEnemyTemplate : EnemyTemplate { override val name = "Test enemy" override val description = "Some enemy standing in your way." override val nameLocalizations = mapOf( "ru" to " -", "ar" to "ؚعض Ø§ÙØ¹Ø¯Ù", "iw" to "×××× ××××", "zh" to "äžäºæµäºº", "ua" to "Ñ " ) override val descriptionLocalizations = mapOf( "ru" to " - .", "ar" to "ÙØµÙ Ø§ÙØ¹Ø¯Ù", "iw" to "ת×××ך ×××××", "zh" to "äžäºæµäººçæè¿°", "ua" to " Ñ Ñ ." ) override val traits = listOf<Trait>() }
ãã³ãã¬ãŒãã®å Žåããã®ã¢ãããŒãã¯éåžžã«é©ããŠããŸããã©ã®èšèªã®ç¿»èš³ãæå®ããããªãå Žåã¯ããã®å¿
èŠã¯ãããŸãã-åžžã«ããã©ã«ãå€ããããŸãããã ããæçµçãªãªããžã§ã¯ãã§ã¯ãè€æ°ã®ç°ãªããã£ãŒã«ãã«è¡ãåºããããªãã§ãããããããã£ãŠã1ã€ãæ®ããŸããããã®ã¿ã€ãã¯çœ®ãæããŸãã class LocalizedString(defaultValue: String, localizations: Map<String, String>) { private val default: String = defaultValue private val values: Map<String, String> = localizations.toMap() operator fun get(lang: String) = values.getOrDefault(lang, default) override fun equals(other: Any?) = when { this === other -> true other !is LocalizedString -> false else -> default == other.default } override fun hashCode(): Int { return default.hashCode() } }
ããã«å¿ããŠãžã§ãã¬ãŒã¿ãŒã³ãŒããä¿®æ£ããŸãã fun generateEnemy(template: EnemyTemplate) = Enemy().apply { name = LocalizedString(template.name, template.nameLocalizations) description = LocalizedString(template.description, template.descriptionLocalizations) template.traits.forEach { addTrait(it) } }
åœç¶ãåãã¢ãããŒããæ®ãã®ã¿ã€ãã®ãã³ãã¬ãŒãã«é©çšããå¿
èŠããããŸãã倿Žã®æºåãã§ããããåé¡ãªã䜿çšã§ããŸãã val language = Locale.getDefault().language val enemyName = enemy.name[language]
ãã®äŸã§ã¯ãèšèªã®ã¿ãèæ
®ãããããŒã«ã©ã€ãºã®ç°¡æããŒãžã§ã³ãæäŸããŠããŸããäžè¬ã«ãã¯ã©ã¹ãªããžã§ã¯ãLocale
ã¯åœãšå°åãå®çŸ©ããŸãããããã¢ããªã±ãŒã·ã§ã³ã§éèŠãªå Žåã¯ãLocalizedString
å€èгãå°ãç°ãªããŸããããšã«ããæºè¶³ããŠããŸãããã³ãã¬ãŒããåŠçããŸããããã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšããããµãŒãã¹ã©ã€ã³ãããŒã«ã©ã€ãºããå¿
èŠããããŸãã幞ããªããšã«ãããã«ResourceBundle
ã¯å¿
èŠãªã¡ã«ããºã ããã¹ãŠå«ãŸããŠããŸãã翻蚳æžã¿ã®ãã¡ã€ã«ãæºåããããŠã³ããŒãæ¹æ³ã倿Žããã ãã§ãã # Game status messages choose_dice_perform_check= : end_of_turn_discard_extra= : : end_of_turn_discard_optional= : : choose_action_before_exploration=, : choose_action_after_exploration= . ? encounter_physical= . . encounter_somatic= . . encounter_mental= . . encounter_verbal= . . encounter_divine= . : die_acquire_success= ! die_acquire_failure= . game_loss_out_of_time= # Die types physical= somatic= mental= verbal= divine= ally= wound= enemy= villain= obstacle= # Hero types and descriptions brawler= hunter= # Various labels avg= bag= bag_size= class= closed= discard= empty= encountered= fail= hand= heros_turn= %s max= min= perform_check= : pile= received_new_die= result= success= sum= time= total= # Action names and descriptions action_confirm_key=ENTER action_confirm_name= action_cancel_key=ESC action_cancel_name= action_explore_location_key=E action_explore_location_name= action_finish_turn_key=F action_finish_turn_name= action_hide_key=H action_bag_name= action_discard_key=D action_discard_name= action_acquire_key=A action_acquire_name= action_leave_key=L action_leave_name= action_forfeit_key=F action_forfeit_name=
èšé²ã®ããã«ç§ã¯èšããŸããïŒãã·ã¢èªã§ãã¬ãŒãºãæžãããšã¯è±èªããã¯ããã«è€éã§ããæ±ºå®çãªã±ãŒã¹ã§åè©ã䜿çšããããæ§å¥ããé¢è±ããå¿
èŠãããå ŽåïŒããã³ãã®ãããªèŠä»¶ã¯å¿
ãæç«ããŸãïŒããŸãèŠä»¶ãæºãããæ¬¡ã«ãµã€ããŒã°ã«ãã£ãŠè¡ãããæ©æ¢°ç¿»èš³ã®ããã«èŠããªãçµæãåŸãããã«æ±ããããªããã°ãªããŸããé¶ã®è³ãæã€ããŸããã¢ã¯ã·ã§ã³ããŒã倿Žããªãããšã«æ³šæããŠãã ãã-以åãšåãããã«ãåŸè
ã¯è±èªãšåãæåã䜿çšããŠå®è¡ãããŸãïŒã¡ãªã¿ã«ãã©ãã³èªä»¥å€ã®ããŒããŒãã¬ã€ã¢ãŠãã§ã¯æ©èœããŸããããããã¯ç§ãã¡ã®ããžãã¹ã§ã¯ãããŸãã-ä»ã®ãšããã¯ãã®ãŸãŸã«ããŠãããŸãïŒã class PropertiesStringLoader(locale: Locale) : StringLoader { private val properties = ResourceBundle.getBundle("text.strings", locale) override fun loadString(key: String) = properties.getString(key) ?: "" }
ã
ãã§ã«è¿°ã¹ãããã«ãResourceBundle
圌èªèº«ãããŒã«ã©ã€ãºãã¡ã€ã«ã®äžããçŸåšã®ãã±ãŒã«ã«æãè¿ããã®ãèŠã€ãã責任ãè² ããŸãããããŠãèŠã€ãããªãå Žåã¯ãããã©ã«ãã®ãã¡ã€ã«ïŒstring.properties
ïŒãååŸããŸãããããŠããã¹ãŠãããŸããããŸã...ããïŒãã£ãïŒ, Unicode
.properties
Java 9. ISO-8859-1 â
ResourceBundle
. , , â . Unicode- â , , :
'\uXXXX'
. , , Java
native2ascii , . :
# Game status messages choose_dice_perform_check=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u0443\u0431\u0438\u043a\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438: end_of_turn_discard_extra=\u041a\u041e\u041d\u0415\u0426 \u0425\u041e\u0414\u0410: \u0421\u0431\u0440\u043e\u0441\u044c\u0442\u0435 \u043b\u0438\u0448\u043d\u0438\u0435 \u043a\u0443\u0431\u0438\u043a\u0438: end_of_turn_discard_optional=\u041a\u041e\u041d\u0415\u0426 \u0425\u041e\u0414\u0410: \u0421\u0431\u0440\u043e\u0441\u044c\u0442\u0435 \u043a\u0443\u0431\u0438\u043a\u0438 \u043f\u043e \u0436\u0435\u043b\u0430\u043d\u0438\u044e: choose_action_before_exploration=\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c: choose_action_after_exploration=\u0418\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e. \u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435? encounter_physical=\u0412\u0441\u0442\u0440\u0435\u0447\u0435\u043d \u0424\u0418\u0417\u0418\u0427\u0415\u0421\u041a\u0418\u0419 \u043a\u0443\u0431\u0438\u043a. \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443. encounter_somatic=\u0412\u0441\u0442\u0440\u0435\u0447\u0435\u043d \u0421\u041e\u041c\u0410\u0422\u0418\u0427\u0415\u0421\u041a\u0418\u0419 \u043a\u0443\u0431\u0438\u043a. \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443. encounter_mental=\u0412\u0441\u0442\u0440\u0435\u0447\u0435\u043d \u041c\u0415\u041d\u0422\u0410\u041b\u042c\u041d\u042b\u0419 \u043a\u0443\u0431\u0438\u043a. \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443. encounter_verbal=\u0412\u0441\u0442\u0440\u0435\u0447\u0435\u043d \u0412\u0415\u0420\u0411\u0410\u041b\u042c\u041d\u042b\u0419 \u043a\u0443\u0431\u0438\u043a. \u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0439\u0442\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443. encounter_divine=\u0412\u0441\u0442\u0440\u0435\u0447\u0435\u043d \u0411\u041e\u0416\u0415\u0421\u0422\u0412\u0415\u041d\u041d\u042b\u0419 \u043a\u0443\u0431\u0438\u043a. \u041c\u043e\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0431\u0435\u0437 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438: die_acquire_success=\u0412\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043d\u043e\u0432\u044b\u0439 \u043a\u0443\u0431\u0438\u043a! die_acquire_failure=\u0412\u0430\u043c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0443\u0431\u0438\u043a. game_loss_out_of_time=\u0423 \u0432\u0430\u0441 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u043e\u0441\u044c \u0432\u0440\u0435\u043c\u044f
. â . â . , IDE ( ) « », â - ( ), IDE, .
, .
getBundle()
, , ,
ResourceBundle.Control
â - .
class PropertiesStringLoader(locale: Locale) : StringLoader { private val properties = ResourceBundle.getBundle( "text.strings", locale, Utf8ResourceBundleControl()) override fun loadString(key: String) = properties.getString(key) ?: "" }
, , :
class Utf8ResourceBundleControl : ResourceBundle.Control() { @Throws(IllegalAccessException::class, InstantiationException::class, IOException::class) override fun newBundle(baseName: String, locale: Locale, format: String, loader: ClassLoader, reload: Boolean): ResourceBundle? { val bundleName = toBundleName(baseName, locale) return when (format) { "java.class" -> super.newBundle(baseName, locale, format, loader, reload) "java.properties" -> with((if ("://" in bundleName) null else toResourceName(bundleName, "properties")) ?: return null) { when { reload -> reload(this, loader) else -> loader.getResourceAsStream(this) }?.let { stream -> InputStreamReader(stream, "UTF-8").use { r -> PropertyResourceBundle(r) } } } else -> throw IllegalArgumentException("Unknown format: $format") } } @Throws(IOException::class) private fun reload(resourceName: String, classLoader: ClassLoader): InputStream { classLoader.getResource(resourceName)?.let { url -> url.openConnection().let { connection -> connection.useCaches = false return connection.getInputStream() } } throw IOException("Unable to load data!") } }
, ⊠, ( ) â ( Kotlin ). â ,
.properties
UTF-8 - .
ããŸããŸãªèšèªã§ã¢ããªã±ãŒã·ã§ã³ã®åäœããã¹ãããããã«ããªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã®èšå®ã倿Žããå¿
èŠã¯ãããŸãããJREã®èµ·åæã«å¿
èŠãªèšèªãæå®ããã ãã§ãã java -Duser.language=ru -jar path_to_project\Dice\target\dice-1.0-jar-with-dependencies.jar
ãŸã Windowsã§äœæ¥ããŠããå Žåã¯ãåé¡ãäºæããŠãã ãã, Windows (cmd.exe) 437 ( DOSLatinUS), â . , UTF-8 , :
chcp 65001
Java , , . :
java -Dfile.encoding=UTF-8 -Duser.language=ru -jar path_to_project\Dice\target\dice-1.0-jar-with-dependencies.jar
, , Unicode- (, Lucida Console)
ãã¹ãŠã®ãšããµã€ãã£ã³ã°ãªåéºã®åŸãçµæã¯èªãããã«äžè¬å€§è¡ã«ç€ºããã倧声ã§å®£èšãããŸãïŒãç§ãã¡ã¯ç¬ã§ã¯ãããŸããïŒã人皮çå¿ å®ãªãªãã·ã§ã³ ãããŠããã¯è¯ãããšã§ãã
ã¹ããã13 ãã¹ãŠããŸãšãã
æ°é
ãã®ããèªè
ã¯ãç¹å®ã®ããã±ãŒãžã®ååã«äžåºŠã ãèšåããæ±ºããŠæ»ã£ãŠããªãã£ãããšã«æ°ä»ããã«éããããŸããããŸããåéçºè
ã¯ãã©ã®ã¯ã©ã¹ãã©ã®ããã±ãŒãžã«é
眮ãããã«é¢ããŠãç¬èªã®èæ
®äºé
ãæã£ãŠããŸãã第äºã«ããããžã§ã¯ãã«åãçµãã«ã€ããŠãããå€ãã®æ°ããã¯ã©ã¹ã远å ããããšãèããå€ãããŸãã第äžã«ãã¢ããªã±ãŒã·ã§ã³ã®æ§é ã®å€æŽã¯ç°¡åã§å®äŸ¡ã§ãïŒãããŠãææ°ã®ããŒãžã§ã³ç®¡çã·ã¹ãã ãç§»è¡ãæ€åºãããããå±¥æŽã倱ãããšã¯ãããŸããïŒãã¯ã©ã¹ãããã±ãŒãžãã¡ãœããã倿°ã®ååã倧èã«å€æŽããŸã-ããã¥ã¡ã³ããæŽæ°ããããšãå¿ããªãã§ãã ãããããã§ããïŒïŒããããŠç§ãã¡ã«æ®ã£ãŠããã®ã¯ããããžã§ã¯ãããŸãšããŠç«ã¡äžããããšã ãã§ããèŠããŠããããã«ãã¡ãœããmain()
ããã§ã«äœæããŸãããä»åºŠã¯ãã®å
容ãåããŸããå¿
èŠãªãã®ïŒ
- ã¹ã¯ãªãããšå°åœ¢;
- ããŒããŒãº
- ã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£
GameInteractor
ã - ã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£
GameRenderer
ããã³StringLoader
; - ã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£
SoundPlayer
ããã³MusicPlayer
; - ã¯ã©ã¹ãªããžã§ã¯ã
Game
ã - ã·ã£ã³ãã³ã®ããã«ã
è¡ããïŒ
fun main(args: Array<String>) { Audio.init(BasicSoundPlayer(), BasicMusicPlayer()) val loader = PropertiesStringLoader(Locale.getDefault()) val renderer = ConsoleGameRenderer(loader) val interactor = ConsoleGameInteractor() val template = TestScenarioTemplate() val scenario = generateScenario(template, 1) val locations = generateLocations(template, 1, heroes.size) val heroes = listOf( generateHero(Hero.Type.BRAWLER, "Brawler"), generateHero(Hero.Type.HUNTER, "Hunter") ) val game = Game(renderer, interactor, scenario, locations, heroes) game.start() }
ç§ãã¡ã¯æåã®å®çšãããã¿ã€ããç«ã¡äžããŠæ¥œãã¿ãŸããè¡ããã¹ããã14ãã²ãŒã ãã©ã³ã¹
ããŒã...ã¹ããã15ããã¹ã
ä»ããããšãæžãããæåã®äœæ¥ãããã¿ã€ãã®ã³ãŒãã®äž»èŠãªéšåã¯ããŠããããã¹ãã®ã«ããã«ã远å ããŠããã ãã...ãã©ã®ããã«ïŒãã£ãä»ïŒã¯ãããã¹ãã¯æåã«èšè¿°ããæ¬¡ã«ã³ãŒããèšè¿°ããªããã°ãªããŸããã§ããïŒãå€ãã®èªè
ã¯ãåäœãã¹ãã®èšè¿°ãäœæ¥ã³ãŒãïŒTDDãã®ä»ã®ãã¡ãã·ã§ããã«ãªæ¹æ³è«ïŒãä»ã®äººã¯æ¿æããŸãïŒå°ãªããšãäœããéçºãå§ãããšããŠãã人ã
ããã¹ãã§é ãã ãŸãããšã¯äœããããŸãããå¥ã®ã«ããã«ãããŒã¹ããŒãã®ééããofãåºããŠãããããã®ãã¹ããå¿
èŠãªçç±ãããããªã-ãã¹ãŠãç§ã«ãšã£ãŠããŸãããããšâç
ã«èšãã§ããã...ãããŠåœŒãã¯ããŒãã§é¡ã«æŒã蟌ãŸããããã«æŒãæ»ãããŸããç§ã¯ã€ããªãã®ãŒç察ç«ãéå§ãå§ããŸããïŒåœŒãã¯ãã§ã«ã€ã³ã¿ãŒãããäžã§ãããã«æºã¡ãŠããŸãïŒããããã£ãŠãç§ã¯ãã¹ãŠã®äººã«éšåçã«åæããŸããã¯ãããã¹ãã¯æã
圹ã«ç«ã¡ãŸãïŒç¹ã«ãé »ç¹ã«å€æŽãããããŸãã¯è€éãªèšç®ã«é¢é£ããã³ãŒãã§ïŒãã¯ãããŠããããã¹ãã¯ãã¹ãŠã®ã³ãŒãã«é©ããŠããŸããïŒããšãã°ããŠãŒã¶ãŒãŸãã¯å€éšã·ã¹ãã ãšã®çžäºäœçšãã«ããŒããŸããïŒãã¯ãããŠããããã¹ã以å€ã«ããããŸãä»ã®çš®ã®å€ãïŒãŸããå°ãªããšã5ã€ãåœåãããïŒããããŠãã¯ããç§ãã¡ã¯ãã¹ããæžãããšã«éäžããŸãã-ç§ãã¡ã®èšäºã¯äœãä»ã®ãã®ã«ã€ããŠã§ããå€ãã®ããã°ã©ããŒïŒç¹ã«åå¿è
ïŒã¯ãã¹ããæ ããŸããå€ãã®äººã¯ãã¢ããªã±ãŒã·ã§ã³ã®æ©èœããã¹ãã§ååã«ã«ããŒãããŠããªããšèšãããšã§æ£åœåããŠããŸããããšãã°ããŠãŒã¶ãŒã€ã³ã¿ãŒãã§ãŒã¹ããã¹ãããããã®ç¹æ®ãªãã¬ãŒã ã¯ãŒã¯ã䜿çšããŠè€éãªæ§é ããã§ã³ã¹ããããããã¢ããªã±ãŒã·ã§ã³ãèµ·åããŠããã¹ãŠãå€èгãšçžäºäœçšã«åé¡ããªããã©ããã確èªããæ¹ãã¯ããã«ç°¡åã§ãããããŠãã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããŠãããšãã«ãç¥ããããŸãRenderer
-ããã ãã§ããããã ããã³ãŒãã®äžã«ã¯ãåäœãã¹ãã®æŠå¿µãåªããŠããã¡ãœããããããŸããããšãã°ããžã§ãã¬ãŒã¿ãŒããããŠããã ãã§ããããã¯çæ³çãªãã©ãã¯ããã¯ã¹ã§ãããã³ãã¬ãŒãã¯å
¥åã«éä¿¡ãããã²ãŒã ã¯ãŒã«ãã®ãªããžã§ã¯ãã¯åºåã§ååŸãããŸããå
éšã§ã¯äœãèµ·ãããŸããããã¹ãããå¿
èŠãããã®ã¯ãŸãã«åœŒã§ããããšãã°ã次ã®ããã«ïŒ
public class DieGeneratorTest { @Test public void testGetMaxLevel() { assertEquals("Max level should be 3", 3, DieGeneratorKt.getMaxLevel()); } @Test public void testDieGenerationSize() { DieTypeFilter filter = new SingleDieTypeFilter(Die.Type.ALLY); List<? extends List<Integer>> allowedSizes = Arrays.asList( null, Arrays.asList(4, 6, 8), Arrays.asList(4, 6, 8, 10), Arrays.asList(6, 8, 10, 12) ); IntStream.rangeClosed(1, 3).forEach(level -> { for (int i = 0; i < 10; i++) { int size = DieGeneratorKt.generateDie(filter, level).getSize(); assertTrue("Incorrect level of die generated: " + size, allowedSizes.get(level).contains(size)); assertTrue("Incorrect die size: " + size, size >= 4); assertTrue("Incorrect die size: " + size, size <= 12); assertTrue("Incorrect die size: " + size, size % 2 == 0); } }); } @Test public void testDieGenerationType() { List<Die.Type> allowedTypes1 = Arrays.asList(Die.Type.PHYSICAL); List<Die.Type> allowedTypes2 = Arrays.asList(Die.Type.PHYSICAL, Die.Type.SOMATIC, Die.Type.MENTAL, Die.Type.VERBAL); List<Die.Type> allowedTypes3 = Arrays.asList(Die.Type.ALLY, Die.Type.VILLAIN, Die.Type.ENEMY); for (int i = 0; i < 10; i++) { Die.Type type1 = DieGeneratorKt.generateDie(new SingleDieTypeFilter(Die.Type.PHYSICAL), 1).getType(); assertTrue("Incorrect die type: " + type1, allowedTypes1.contains(type1)); Die.Type type2 = DieGeneratorKt.generateDie(new StatsDieTypeFilter(), 1).getType(); assertTrue("Incorrect die type: " + type2, allowedTypes2.contains(type2)); Die.Type type3 = DieGeneratorKt.generateDie(new MultipleDieTypeFilter(Die.Type.ALLY, Die.Type.VILLAIN, Die.Type.ENEMY), 1).getType(); assertTrue("Incorrect die type: " + type3, allowedTypes3.contains(type3)); } } }
ãŸãã¯ïŒ
public class BagGeneratorTest { @Test public void testGenerateBag() { BagTemplate template1 = new BagTemplate(); template1.addPlan(0, 10, new SingleDieTypeFilter(Die.Type.PHYSICAL)); template1.addPlan(5, 5, new SingleDieTypeFilter(Die.Type.SOMATIC)); template1.setFixedDieCount(null); BagTemplate template2 = new BagTemplate(); template2.addPlan(10, 10, new SingleDieTypeFilter(Die.Type.DIVINE)); template2.setFixedDieCount(5); BagTemplate template3 = new BagTemplate(); template3.addPlan(10, 10, new SingleDieTypeFilter(Die.Type.ALLY)); template3.setFixedDieCount(50); for (int i = 0; i < 10; i++) { Bag bag1 = BagGeneratorKt.generateBag(template1, 1); assertTrue("Incorrect bag size: " + bag1.getSize(), bag1.getSize() >= 5 && bag1.getSize() <= 15); assertEquals("Incorrect number of SOMATIC dice", 5, bag1.examine().stream().filter(d -> d.getType() == Die.Type.SOMATIC).count()); Bag bag2 = BagGeneratorKt.generateBag(template2, 1); assertEquals("Incorrect bag size", 5, bag2.getSize()); Bag bag3 = BagGeneratorKt.generateBag(template3, 1); assertEquals("Incorrect bag size", 50, bag3.getSize()); List<Die.Type> dieTypes3 = bag3.examine().stream().map(Die::getType).distinct().collect(Collectors.toList()); assertEquals("Incorrect die types", 1, dieTypes3.size()); assertEquals("Incorrect die types", Die.Type.ALLY, dieTypes3.get(0)); } } }
ãŸãã¯ãã®ããã«ïŒ public class LocationGeneratorTest { private void testLocationGeneration(String name, LocationTemplate template) { System.out.println("Template: " + template.getName()); assertEquals("Incorrect template type", name, template.getName()); IntStream.rangeClosed(1, 3).forEach(level -> { Location location = LocationGeneratorKt.generateLocation(template, level); assertEquals("Incorrect location type", name, location.getName().get("")); assertTrue("Location not open by default", location.isOpen()); int closingDifficulty = location.getClosingDifficulty(); assertTrue("Closing difficulty too small", closingDifficulty > 0); assertEquals("Incorrect closing difficulty", closingDifficulty, template.getBasicClosingDifficulty() + level * 2); Bag bag = location.getBag(); assertNotNull("Bag is null", bag); assertTrue("Bag is empty", location.getBag().getSize() > 0); Deck<Enemy> enemies = location.getEnemies(); assertNotNull("Enemies are null", enemies); assertEquals("Incorrect enemy threat count", enemies.getSize(), template.getEnemyCardsCount()); if (bag.drawOfType(Die.Type.ENEMY) != null) { assertTrue("Enemy cards not specified", enemies.getSize() > 0); } Deck<Obstacle> obstacles = location.getObstacles(); assertNotNull("Obstacles are null", obstacles); assertEquals("Incorrect obstacle threat count", obstacles.getSize(), template.getObstacleCardsCount()); List<SpecialRule> specialRules = location.getSpecialRules(); assertNotNull("SpecialRules are null", specialRules); }); } @Test public void testGenerateLocation() { testLocationGeneration("Test Location", new TestLocationTemplate()); testLocationGeneration("Test Location 2", new TestLocationTemplate2()); } }
ãã¹ããããã¹ããããã¹ãããïŒãããã¯äœïŒ Java ???ãããªãã¯çè§£ããŠããŸããããã«ããžã§ãã¬ãŒã¿ãŒèªäœã®å®è£
ãéå§ããåã«ãæåã«ãã®ãããªãã¹ããèšè¿°ããããšããå§ãããŸãããã¡ããããã¹ã察象ã®ã³ãŒãã¯éåžžã«åçŽã§ãããã¡ãœããã¯ãã¹ããªãã§åããŠåäœããå¯èœæ§ãé«ãã§ããããã¹ããäœæãããšããããæ°žä¹
ã«å¿ããå°æ¥èµ·ããããåé¡ããä¿è·ããŸãïŒãã®è§£æ±ºã«ã¯å€ãã®æéãããããŸããç¹ã«éçºã®éå§æããïŒ 5幎ãçµéããã¡ãœããå
ã®ãã¹ãŠãããã§ã©ã®ããã«æ©èœãããããã§ã«å¿ããŠããŸããïŒããŸãããã¹ãã倱æããããã«çªç¶ãããžã§ã¯ãã®åéã忢ããå Žåãã·ã¹ãã èŠä»¶ã倿Žãããå€ããã¹ãã§ããããæºããããªããªã£ãçç±ãããããŸãïŒã©ãæããŸãããïŒïŒããã®ä»ã
ã¯ã©ã¹HandMaskRule
ãšãã®çžç¶äººãèŠããŠããŸããïŒããæç¹ã§ãã¹ãã«ã䜿çšããããã«ããŒããŒã3ã€ã®ãµã€ã³ããæããåãå¿
èŠãããããããã®ãµã€ã³ãã®çš®é¡ã«ã¯å³ããå¶éãããããšãæ³åããŠãã ããïŒããšãã°ããæåã®ãµã€ã³ãã¯éãç·ãçœã2çªç®ã¯é»è²ãçœãéããããŠ3çªç®-éãŸãã¯çŽ« "-ããªãã¯å°é£ãæããŸããïŒïŒãã¯ã©ã¹ã®å®è£
ã«ã¢ãããŒãããæ¹æ³ã¯ïŒãŸã...ãŸããå
¥åãã©ã¡ãŒã¿ãšåºåãã©ã¡ãŒã¿ã決ããããšãã§ããŸããæããã«ã3ã€ã®é
åïŒãŸãã¯ã»ããïŒãåãå
¥ããã¯ã©ã¹ãå¿
èŠã§ããåé
åã«ã¯ãããããã1çªç®ã2çªç®ã3çªç®ã®ãã¥ãŒãã®æå¹ãªåãå«ãŸããŠããŸããããããäœïŒ
ã€ã¶ããŸããïŒååž°ïŒäœããèŠéãããã©ããªããŸããïŒæ·±ãå
¥ãå£ãäœããŸããã¯ã©ã¹ã¡ãœããã®å®è£
ãå»¶æãããã¹ããäœæããŸããèŠä»¶ã¯ã·ã³ãã«ã§çè§£ãããããæ£åŒã«å®åŒåãããŠããããã§ãããããŠãããã€ãã®ãã¹ããäœæããæ¹ãè¯ãã§ããã...ããããããã§ã¯ãã®ãããªãã®ãæ€èšããŸãïŒ public class TripleDieHandMaskRuleTest { private Hand hand; @Before public void init() { hand = new Hand(10); hand.addDie(new Die(Die.Type.PHYSICAL, 4));
ããã¯éªšãæããŸãããéå§ãããŸã§ã¯èŠããã»ã©ã§ã¯ãããŸããïŒããæç¹ã§ãããã«æ¥œãããªããŸãïŒããããããã®ãããªãã¹ãïŒããã³å¥ã®æ©äŒã®ããã®ããã€ãã®ãã¹ãïŒãæžããšãããªãã¯çªç¶èœã¡çããšèªä¿¡ãæããŸããããã§ãå°ããªã¿ã€ããã¹ã§ã¡ãœãããå°ç¡ãã«ãªããäžæå¿«ãªé©ãã«ã€ãªãããæäœæ¥ã§ãã¹ãããã®ãã¯ããã«é£ãããªããŸããå°ããã€ãã¯ã©ã¹ã®å¿
èŠãªã¡ãœãããå®è£
ãå§ããŸãããããŠæåŸã«ãã¹ããå®è¡ããŠãã©ããã§ééããç¯ããããšã確èªããŸããåé¡ç®æãèŠã€ããŠæžãçŽããŠãã ãããæºåãæŽããŸã§ç¹°ãè¿ããŸãã class TripleDieHandMaskRule( hand: Hand, types1: Array<Die.Type>, types2: Array<Die.Type>, types3: Array<Die.Type>) : HandMaskRule(hand) { private val types1 = types1.toSet() private val types2 = types2.toSet() private val types3 = types3.toSet() override fun checkMask(mask: HandMask): Boolean { if (mask.positionCount + mask.allyPositionCount != 3) { return false } return getCheckedDice(mask).asSequence() .filter { it.type in types1 } .any { d1 -> getCheckedDice(mask) .filter { d2 -> d2 !== d1 } .filter { it.type in types2 } .any { d2 -> getCheckedDice(mask) .filter { d3 -> d3 !== d1 } .filter { d3 -> d3 !== d2 } .any { it.type in types3 } } } } override fun isPositionActive(mask: HandMask, position: Int): Boolean { if (mask.checkPosition(position)) { return true } val die = hand.dieAt(position) ?: return false return when (mask.positionCount + mask.allyPositionCount) { 0 -> die.type in types1 || die.type in types2 || die.type in types3 1 -> with(getCheckedDice(mask).first()) { (this.type in types1 && (die.type in types2 || die.type in types3)) || (this.type in types2 && (die.type in types1 || die.type in types3)) || (this.type in types3 && (die.type in types1 || die.type in types2)) } 2-> with(getCheckedDice(mask)) { val d1 = this[0] val d2 = this[1] (d1.type in types1 && d2.type in types2 && die.type in types3) || (d2.type in types1 && d1.type in types2 && die.type in types3) || (d1.type in types1 && d2.type in types3 && die.type in types2) || (d2.type in types1 && d1.type in types3 && die.type in types2) || (d1.type in types2 && d2.type in types3 && die.type in types1) || (d2.type in types2 && d1.type in types3 && die.type in types1) } 3 -> false else -> false } } override fun isAllyPositionActive(mask: HandMask, position: Int): Boolean { if (mask.checkAllyPosition(position)) { return true } if (hand.allyDieAt(position) == null) { return false } return when (mask.positionCount + mask.allyPositionCount) { 0 -> ALLY in types1 || ALLY in types2 || ALLY in types3 1 -> with(getCheckedDice(mask).first()) { (this.type in types1 && (ALLY in types2 || ALLY in types3)) || (this.type in types2 && (ALLY in types1 || ALLY in types3)) || (this.type in types3 && (ALLY in types1 || ALLY in types2)) } 2-> with(getCheckedDice(mask)) { val d1 = this[0] val d2 = this[1] (d1.type in types1 && d2.type in types2 && ALLY in types3) || (d2.type in types1 && d1.type in types2 && ALLY in types3) || (d1.type in types1 && d2.type in types3 && ALLY in types2) || (d2.type in types1 && d1.type in types3 && ALLY in types2) || (d1.type in types2 && d2.type in types3 && ALLY in types1) || (d2.type in types2 && d1.type in types3 && ALLY in types1) } 3 -> false else -> false } } }
ãã®ãããªæ©èœãããç°¡åã«å®è£
ããæ¹æ³ã«ã€ããŠã¢ã€ãã¢ãããå Žåã¯ãã³ã¡ã³ããæè¿ããŸãããããŠããã¹ããæžãããšã§ãã®ã¯ã©ã¹ã®å®è£
ãå§ããã®ã«ååãªã»ã©é ãè¯ãã£ããšä¿¡ããããªãã»ã©å¬ããã§ããããããŠãç§ã¯<...>ã<...>éåžžã«<...>ãããã<...>ã§ããå
¥ããïŒ<...>æ»ãïŒ<...>ã®ã£ããã«ïŒãã¹ããã16ãã¢ãžã¥ãŒã«æ§
äºæ³éããæçããåäŸã¯äžç芪ã®ä¿è·äžã«ããããšã¯ã§ããŸãããé
ããæ©ãããèªåã®éãéžãã§å€§èã«éãé²ã¿ãå°é£ãæ··ä¹±ãå
æããªããã°ãªããŸããããã®ãããç§ãã¡ãéçºããã³ã³ããŒãã³ãã¯éåžžã«æçããŠããããã1ã€ã®å±æ ¹ã®äžã§crå±ã«ãªããŸãããããããããã€ãã®éšåã«åããæãæ¥ãŸãããããªãäºçްãªäœæ¥ã«çŽé¢ããŠããŸãããããŸã§ã«äœæããããã¹ãŠã®ã¯ã©ã¹ã3ã€ã®ã°ã«ãŒãã«åããå¿
èŠããããŸãã- åºæ¬æ©èœïŒã¢ãžã¥ãŒã«ãã²ãŒã ãšã³ãžã³ãã³ãã¯ã¿ã€ã³ã¿ãŒãã§ã€ã¹ããã©ãããã©ãŒã ã«äŸåããªãå®è£
ïŒã³ã¢ïŒ;
- ã·ããªãªãå°åœ¢ãæµãé害ç©ã®ãã³ãã¬ãŒã-ãããããã¢ããã³ãã£ãŒãïŒã¢ããã³ãã£ãŒïŒã®ã³ã³ããŒãã³ãã
- ç¹å®ã®ãã©ãããã©ãŒã ã«åºæã®ã€ã³ã¿ãŒãã§ã€ã¹ã®ç¹å®ã®å®è£
ïŒãã®å Žåãã³ã³ãœãŒã«ã¢ããªã±ãŒã·ã§ã³ïŒcliïŒã
ãã®åé¢ã®çµæã¯ãæçµçã«æ¬¡ã®å³ã®ããã«ãªããŸããã·ã§ãŒã®æåŸã®ä¿³åªã®ããã«ãç§ãã¡ã®ä»æ¥ã®ããŒããŒã¯åã³å
šåã§èå°ã«ç»å ŽããŸã 远å ã®ãããžã§ã¯ããäœæãã察å¿ããã¯ã©ã¹ã転éããŸãããããŠããããžã§ã¯ãéã®çžäºäœçšãæ£ããæ§æããå¿
èŠããããŸããã³ã¢ãããžã§ã¯ããã®ãããžã§ã¯ãã¯çŽç²ãªãšã³ãžã³ã§ããç¹å®ã®ã¯ã©ã¹ã¯ãã¹ãŠä»ã®ãããžã§ã¯ãã«è»¢éãããŸãã-åºæ¬çãªæ©èœã§ããã³ã¢ã®ã¿ãæ®ããŸãããå¿
èŠã«å¿ããŠã©ã€ãã©ãªãèµ·åã¯ã©ã¹ã¯ãããããŸãããããã±ãŒãžããã«ãããå¿
èŠããããŸããããã®ãããžã§ã¯ãã®ã¢ã»ã³ããªã¯ãããŒã«ã«ã®Mavenãªããžããªã§ãã¹ãããïŒè©³çްã¯åŸè¿°ïŒãä»ã®ãããžã§ã¯ãã§äŸåé¢ä¿ãšããŠäœ¿çšãããŸãããã¡ã€ã«pom.xml
ã¯æ¬¡ã®ãšããã§ãã <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my.company</groupId> <artifactId>dice-core</artifactId> <version>1.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>${kotlin.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit-dep</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.jetbrains.kotlin</groupId> </plugin> </plugins> </build> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <kotlin.version>1.3.20</kotlin.version> <kotlin.compiler.incremental>true</kotlin.compiler.incremental> </properties> </project>
ãããããæ¬¡ã®ããã«åéããŸãã mvn -f "path_to_project/DiceCore/pom.xml" install
Cliãããžã§ã¯ãããã¯ãã¢ããªã±ãŒã·ã§ã³ãžã®ãšã³ããªãã€ã³ãã§ã-ãšã³ããŠãŒã¶ãŒã察話ããã®ã¯ãã®ãããžã§ã¯ãã§ããã«ãŒãã«ã¯äŸåé¢ä¿ãšããŠäœ¿çšãããŸãããã®äŸã§ã¯ã³ã³ãœãŒã«ã§äœæ¥ããŠããããããããžã§ã¯ãã«ã¯ããã§äœæ¥ããããã«å¿
èŠãªã¯ã©ã¹ãå«ãŸããŸãïŒçªç¶ã³ãŒããŒã¡ãŒã«ãŒã§ã²ãŒã ãéå§ãããå Žåã¯ãåã«ãã®ãããžã§ã¯ãã察å¿ããå®è£
ã®åæ§ã®ãããžã§ã¯ãã«çœ®ãæããŸãïŒãããã«ãªãœãŒã¹ïŒã©ã€ã³ããªãŒãã£ãªãã¡ã€ã«ãªã©ïŒã远å ããŸããå€éšã©ã€ãã©ãªãžã®äŸåé¢ä¿ã¯ãã¡ã€ã«ã«pom.xml
転éãããŸãïŒ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my.company</groupId> <artifactId>dice-cli</artifactId> <version>1.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>${kotlin.version}</version> </dependency> <dependency> <groupId>my.company</groupId> <artifactId>dice-core</artifactId> <version>1.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.fusesource.jansi</groupId> <artifactId>jansi</artifactId> <version>1.17.1</version> <scope>compile</scope> </dependency> <dependency> <groupId>jline</groupId> <artifactId>jline</artifactId> <version>2.14.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.googlecode.soundlibs</groupId> <artifactId>jlayer</artifactId> <version>1.0.1.4</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.jetbrains.kotlin</groupId> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.6</version> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>my.company.dice.MainKt</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <kotlin.version>1.3.20</kotlin.version> <kotlin.compiler.incremental>true</kotlin.compiler.incremental> </properties> </project>
æ¢ã«èŠããã®ãããžã§ã¯ãããã«ãããŠå®è¡ããã¹ã¯ãªãã-ç¹°ãè¿ããŸãããåéºæåŸã«ãå¥ã®ãããžã§ã¯ãã§ãããããåãåºããŸããã€ãŸããããªãã®äŒç€Ÿã®ã·ããªãªéšéã®ã¹ã¿ãããæ³åã§ãããã²ãŒã äžçã®ãã¹ãŠã®ã·ããªãªãå°åœ¢ãæµãããã³ãã®ä»ã®ãŠããŒã¯ãªãªããžã§ã¯ãïŒãŸãããããŸã§ã®ãšãããç§ãã¡èªèº«ã®ç
æ°ã®æ³ååã ãã§ã-ç§ãã¡ã¯ãŸã ãã®å°åã§å¯äžã®ã²ãŒã ãã¶ã€ããŒã§ãïŒãã¢ã€ãã¢ã¯ãã¹ã¯ãªãããã»ããã«ã°ã«ãŒãåãïŒã¢ããã³ãã£ãŒïŒããã®ãããªã»ãããåå¥ã®ãããžã§ã¯ããšããŠé
åžããããšã§ãïŒããŒãã²ãŒã ããããªã²ãŒã ã®äžçã§è¡ãããæ¹æ³ãšåæ§ïŒãã€ãŸããjarã¢ãŒã«ã€ããåéããããããå¥ã®ãã©ã«ããŒã«é
眮ããŠãã²ãŒã ãšã³ãžã³ããã®ãã©ã«ããŒãã¹ãã£ã³ããããã«å«ãŸãããã¹ãŠã®ã¢ããã³ãã£ãŒãèªåçã«æ¥ç¶ããããã«ããŸãããã ãããã®ã¢ãããŒãã®æè¡çãªå®è£
ã«ã¯éåžžã«å€ãã®å°é£ã䌎ããŸããã©ãããå§ããŸããïŒ
ãŸãããŸããç¹å®ã®Javaã¯ã©ã¹ã®åœ¢åŒã§ãã³ãã¬ãŒããé
åžããŠãããšããäºå®ããïŒãããç§ãbeatããoldã-ç§ã¯ãããäºèŠããŠããŸããïŒããã®å Žåããããã®ã¯ã©ã¹ã¯èµ·åæã«ã¢ããªã±ãŒã·ã§ã³ã®ã¯ã©ã¹ãã¹ã«ååšããå¿
èŠããããŸãããã®èŠä»¶ã匷å¶ããããšã¯é£ãããããŸãããé©åãªç°å¢å€æ°ã«jarãã¡ã€ã«ãæç€ºçã«ç»é²ããŸãïŒJava 6以éã*- wildcardsã䜿çšã§ããŸãïŒã java -classpath "path_to_project/DiceCli/target/adventures/*" -jar path_to_project/DiceCli/target/dice-1.0-jar-with-dependencies.jar
ãã°ãããŸãã¯äœïŒ -jarã¹ã€ããã䜿çšãããšã-classpathã¹ã€ããã¯ç¡èŠãããŸãïŒããã ããããã¯æ©èœããŸãããå®è¡å¯èœjarã¢ãŒã«ã€ãã®ã¯ã©ã¹ãã¹ã¯ãå
éšãã¡ã€ã«ã«æç€ºçã«èšè¿°ããå¿
èŠããããŸãMETA-INF/MANIFEST.MF
ïŒã»ã¯ã·ã§ã³ã®ååã¯- Claspath:
ïŒããã®ãããç¹å¥ãªãã©ã°ã€ã³ãå©çšå¯èœã§ãïŒmaven-compiler-pluginãŸãã¯ãææªã®å Žåãmaven-assembly-pluginïŒããã ãããããã§ã¹ãå
ã®ã¯ã€ã«ãã«ãŒãã¯æ©èœããŸãããäŸåããjarãã¡ã€ã«ã®ååãæç€ºçã«æå®ããå¿
èŠããããŸããã€ãŸããããããäºåã«ç¥ãããšã§ããããã¯ãç§ãã¡ã®å Žåã¯åé¡ã§ãããšã«ãããç§ã¯ãããæãã§ããŸããã§ããããããžã§ã¯ããåã³ã³ãã€ã«ããå¿
èŠããªãããã«ãããã£ãããã©ã«ããŒãžadventures/
ä»»æã®æ°ã®ã¢ããã³ãã£ãŒãæããããšãã§ãããããå®è¡äžã«ãã¹ãŠã®ã¢ããã³ãã£ãŒãã²ãŒã ãšã³ãžã³ã«è¡šç€ºãããŸãããæ®å¿µãªãããäžèŠæãããªæ©èœã¯ãJavaã®äžçã®æšæºçãªè¡šçŸãè¶
ããŠããŸãããããã£ãŠãããã¯æè¿ãããŸãããç¬ç«ããåéºãåºããã«ã¯ãç°ãªãã¢ãããŒããå®è£
ããå¿
èŠããããŸããã©ã£ã¡ïŒ
ç§ã¯ç¥ããŸãããã³ã¡ã³ãã«æžããŠãã ãã-確ãã«èª°ããã¹ããŒããªã¢ã€ãã¢ãæã£ãŠããŸãããããŸã§ã¯ããããŸãããååãç¥ããªããŠãããããžã§ã¯ããåã³ã³ãã€ã«ããªããŠããã¯ã©ã¹ãã¹ã«äŸåé¢ä¿ãåçã«è¿œå ã§ããå°ããªïŒãŸãã¯èŠãç®ã«ãã£ãŠã¯å€§ããªïŒããªãã¯ããããŸããWindowsã®å ŽåïŒ @ECHO OFF call "path_to_maven\mvn.bat" -f "path_to_project\DiceCore\pom.xml" install call "path_to_maven\mvn.bat" -f "path_to_project\DiceCli\pom.xml" package call "path_to_maven\mvn.bat" -f "path_to_project\TestAdventure\pom.xml" package mkdir path_to_project\DiceCli\target\adventures copy "path_to_project\TestAdventure\target\test-adventure-1.0.jar" path_to_project\DiceCli\target\adventures\ chcp 65001 cd path_to_project\DiceCli\target\ java -Dfile.encoding=UTF-8 -cp "dice-cli-1.0-jar-with-dependencies.jar;adventures\*" my.company.dice.MainKt pause
Unixã®å ŽåïŒ
ãããŠãããã«ããªãã¯ããããŸããããŒã䜿çšãã代ããã«-jar
ãCliãããžã§ã¯ããã¯ã©ã¹ãã¹ã«è¿œå ãããã®äžã«å«ãŸããã¯ã©ã¹ããšã³ããªãã€ã³ããšããŠæç€ºçã«æå®ããŸãMainKt
ãããã«ãããã§ãã©ã«ããããã¹ãŠã®ã¢ãŒã«ã€ããæ¥ç¶ããŸãadventures/
ããã®æ²ãã£ã決å®ãã©ãã»ã©å€§ããããããäžåºŠç€ºãå¿
èŠã¯ãããŸãã-ç§èªèº«ãæè¬ããŸããã³ã¡ã³ãã§ããªãã®ã¢ã€ãã¢ãææ¡ããŠãã ããããé¡ãããŸãã ïŒà²¥ï¹à²¥ïŒ
ã¹ããã17ã ãããã
å°ãæè©ãç§ãã¡ã®èšäºã¯ã¯ãŒã¯ãããŒã®æè¡çãªåŽé¢ã«é¢ãããã®ã§ãããã²ãŒã ã¯åãªããœãããŠã§ã¢ã³ãŒãã§ã¯ãããŸããããããã¯é¢çœãã€ãã³ããšæŽ»æ°ã®ãããã£ã©ã¯ã¿ãŒãåãããšããµã€ãã£ã³ã°ãªäžçã§ãããããªãã¯é ã§æãã£ããé£ã³èŸŒã¿ãçŸå®ã®äžçãæŸæ£ããŸãããã®ãããªäžçã¯ããããç¬èªã®æ¹æ³ã§çãããç¬èªã®æ¹æ³ã§è峿·±ããã®ã§ããããã®å€ãã¯äœå¹Žãçµã£ãä»ã§ãèŠããŠããŸããããªãã®äžçãæž©ããæ°æã¡ã§æãåºãããããªãããããçãããŠé¢çœãããŠãã ãããããã§ç§ãã¡ã¯ã¹ã¯ãªããã©ã€ã¿ãŒã§ã¯ãªãããã°ã©ããŒã§ããããšã¯ç¥ã£ãŠããŸãããã²ãŒã ã®ãžã£ã³ã«ã®ç©èªã®èŠçŽ ïŒçµéšã®ããã²ãŒããŒãããã§ããïŒã«ã€ããŠã®åºæ¬çãªã¢ã€ãã¢ã¯ãããŸããã©ããªæ¬ã§ãããã§ãããã¹ããŒãªãŒã«ã¯ç®ïŒäž»äººå
¬ãçŽé¢ããŠããåé¡ãåŸã
ã«èª¬æããŸãïŒãçºå±ã2ã3åã®è峿·±ãã¿ãŒã³ãã¯ã©ã€ããã¯ã¹ïŒèªè
ãè奮ããŠåãã€ããŠæ¯ãããã®ãå¿ãããšãã®ããããã®æãæ¥æ§ã®ç¬éïŒãšåŠèªïŒã©ã®ã€ãã³ããåŸã
ã«è«ççãªçµè«ã«éãããïŒãæ§ãããªè¡šçŸãè«ççãªæ ¹æ ã®ãªãããšãããããã®ç©Žãé¿ããŠãã ããããã¹ãŠã®éå§è¡ã¯é©åãªçµè«ã«éããã¯ãã§ããããŠãç§ãã¡ã®è©±ãä»ã®äººã«èªãã§ã¿ãŸããã-åŽããã®å
¬å¹³ãªå€èгã¯ãäœãããæ¬ é¥ãçè§£ããããããæéå
ã«ä¿®æ£ããã®ã«éåžžã«ãã圹ç«ã¡ãŸããã²ãŒã ã®ãããã, , . , : ( ) ( ), . , .
â , . , , .
, , - . , , , , . .
幞éãªããšã«ãç§ã¯Tolkienã§ã¯ãããŸãããã²ãŒã ã®äžçãããŸã詳ãã説æããŠããŸããã§ãããããããååã«è峿·±ããã®ã«ããããšããŸãããåæã«ã圌ã¯ããã€ãã®ãããŸãããå°å
¥ããããšãèš±å¯ããŸãããåãããŸããã¯èªåã®ããæ¹ã§èªç±ã«è§£éã§ããŸããããšãã°ãå°å»ºå¶åºŠãšçŸä»£ã®æ°äž»çå¶åºŠãéªæªãªæŽåãçµç¹çãªç¯çœªã°ã«ãŒããæé«ã®ç®æšãšåœããåã®çåãå±
é
å±ã§ã®ãã¹ä¹ããšæŠã-ãã£ã©ã¯ã¿ãŒãäœããã®çç±ã§æ®åœ±ããŸãïŒåŒ/ã¯ãã¹ããŠããŸãã¯ã¢ãµã«ãã©ã€ãã«ãããäžçã«ã¯éæ³ã®ãããªãã®ãããïŒãã®ååšãæŠè¡çãªèœåã«ã²ãŒã ãã¬ã€ã远å ããŸãïŒãç¥ç§äž»çŸ©ã®èŠçŽ ïŒããã¹ãå§¿ïŒããããŸããç§ã¯é°è¬ã®æ±ºãŸãæå¥ãå¹»æ³çãªæ¶è²»è²¡ããé¢ããããšæã£ãŠããŸãã-ããããã¹ãŠã®ãšã«ããããŒã ããã©ãŽã³ãé»ããããŠçµ¶å¯Ÿçãªäžçã®æªïŒãšåæ§ã«ïŒéžæãããããŒããŒãå€ä»£ã®äºèšãã¹ãŒããŒã¢ãŒãã£ãã¡ã¯ããå£®å€§ãªæŠã...ïŒãŸããç§ã¯å®éã«äžçãçãçããšãããããšæã£ãã®ã§ãåãã£ã©ã¯ã¿ãŒãïŒãã€ããŒãªãã®ã§ãïŒåºäŒã£ãŠãã²ãŒã ã®ä»çµã¿ã®èŠçŽ ãäžçã®æ³åã«é©åããããŒããŒã®æé·ãèªç¶ã«çºçããå Žæã®æµãé害ç©ã®ååšãå Žæèªäœã®ç¹åŸŽã«ãã£ãŠè«ççã«æ£åœåãããããã«ãèªåã®ç©èªãšåæ©ä»ããæã¡ãŸãã...ãªã©ãæ®å¿µãªããšã«ããã®é¡æã¯æ®é
·ãªåè«ãæŒããéçºããã»ã¹ãéåžžã«é
ãããã²ãŒã ã®æ
£ç¿ããé¢ããããšã¯åžžã«å¯èœã§ã¯ãããŸããã§ãããããã«ãããããããæçµè£œåããã®æºè¶³åºŠã¯æ¡éãã«å€§ããããšã倿ããŸããããã®ãã¹ãŠã§ç§ã¯äœãèšãããã§ããïŒããèãæãããè峿·±ãããããã¯ããã»ã©å¿
èŠã§ã¯ãªããããããŸããããããªãã®ã²ãŒã ã¯ãã®ååšã«èŠããããšã¯ãããŸããïŒããããããã¬ã€ã€ãŒã¯ãããæ¥œããã§ããããææªã®å Žåã圌ãã¯åã«ãããç¡èŠããŸãããããŠãç¹ã«ç±å¿ãªäººã¯ãã¹ããŒãªãŒãã©ã®ããã«çµããããç¥ãããã«ãã²ãŒã ã«ããã€ãã®æ©èœçãªæ¬ é¥ãèš±ãããããŸããæ¬¡ã¯ïŒ
ããã«ããã°ã©ãã³ã°ãçµäºããã²ãŒã ãã¶ã€ã³ãéå§ãããŸããä»ã¯ã³ãŒããæžãã®ã§ã¯ãªããã¹ã¯ãªãããå ŽæãæµãçèããŸããããããã¹ãŠã®ããã§ããããªãããŸã äžäººã§åããŠãããªããç§ã¯ããªããç¥çŠããŸã-ããªãã¯ã»ãšãã©ã®ã²ãŒã ãããžã§ã¯ããæ¥ãæ®µéã«éããŸãããå€§èŠæš¡ãªAAAã¹ã¿ãžãªã§ã¯ãç¹å¥ãªäººã
ããã¶ã€ããŒãèæ¬å®¶ãšããŠåããŠããããã®ããã«ãéãåãåããŸã-圌ãã¯åã«è¡ãå
ããããŸãããããããç§ãã¡ã«ã¯ããããã®éžæè¢ããããŸãïŒæ£æ©ã«è¡ããé£äºããããå¹³å¡ãªæ¹æ³ã§å¯ã-ããããããã§ã§ããããšã¯ãèç©ãããçµéšãšç¥èã䜿çšããŠãæ°ãããããžã§ã¯ããéå§ããããšã§ããããªãããŸã ããã«ããŠããã¹ãŠã®è²»çšã§ç¶ç¶ãããå Žåã¯ãå°é£ã«åããŠãã ãããæéã®äžè¶³ãæ ãåµé çãªã€ã³ã¹ãã¬ãŒã·ã§ã³ã®æ¬ åŠ-äœããåžžã«ããªããããããŸããããããã¹ãŠã®é害ãå
æããã®ã¯ç°¡åã§ã¯ãããŸããïŒãã®ãããã¯ã«ã€ããŠã¯å€ãã®èšäºãæžãããŠããŸãïŒããå¯èœã§ãããŸãããããžã§ã¯ãã®ãããªãçºå±ãæ
éã«èšç»ããããšããå§ãããŸãã幞ããªããšã«ãç§ãã¡ã¯åã³ã®ããã«åããŠããŸããåºç瀟ã¯ç§ãã¡ãæŒãä»ããŸããã誰ãç¹å®ã®æéãå®ãå¿
èŠã¯ãããŸããããããžã§ã¯ãã®ãããŒããããããäœæããäž»èŠãªæ®µéãç¹å®ããïŒåæ°ãããå Žåã¯ïŒå®è£
ã®æ¡ä»¶ãæŠç®ããŸããèªåã§ããŒãããã¯ãå
¥æãïŒé»ååã§ããŸãïŒããã®äžã«ããã¢ã€ãã¢ãçµ¶ããæžãçããŠãã ããïŒå€äžã«çªç¶ç®ãèŠããŠãïŒã衚ã§é²æç¶æ³ãããŒã¯ããŸãïŒããšãã°ããã®ãããªïŒãŸãã¯ãã®ä»ã®è£å©ãéå§ããã¥ã¡ã³ãïŒå°æ¥ã®å·šå€§ãªãã¡ã³ã³ãã¥ããã£ã®ããã®å€éšãå
¬éïŒããšãã°wikiïŒãšãèªåèªèº«ã®ããã®å
éšïŒãªã³ã¯ãå
±æããŸããïŒ-ä¿¡ããŠãã ãããäžè¬çã«ãã²ãŒã ã«é¢ããä»éæ
å ±ãã§ããã ãå€ãæžããŸããã²ãŒã èªäœãæžãããšãå¿ããªãã§ãã ãããåºæ¬çãªãªãã·ã§ã³ãææ¡ããŸããããå
·äœçãªã¢ããã€ã¹ã¯ããŸãããåèªãèªåã®äœæ¥ããã»ã¹ãæŽçããã®ããã䟿å©ãªæ¹æ³ãèªåã§æ±ºå®ããŸãããããã§ããã²ãŒã ã®ãã©ã³ã¹ã«ã€ããŠã¯è©±ããããªãã®ã§ããïŒãå®ç§ãªã²ãŒã ãåããŠäœæããŠãããŸããããªããšããäºå®ã«ããã«åããŸããããåäœãããããã¿ã€ãã¯è¯ãã§ã-æåã¯ãããžã§ã¯ãã®å®è¡å¯èœæ§ã瀺ããããªããçŽåŸãããã倱æããããç¶ç¶ãã䟡å€ã¯ãããŸããïŒããšããéåžžã«éèŠãªè³ªåã«çããŸãããããã圌ã¯ä»ã®å€ãã®è³ªåã«ã¯çããŸããããã®äž»ãªè³ªåã¯ããããããé·æçã«ç§ã®ã²ãŒã ããã¬ã€ããã®ã¯é¢çœãã§ããããïŒãã§ãããã®ããŒãã«ã€ããŠã¯ãéåžžã«å€ãã®çè«ãšèšäºããããŸãïŒç¹°ãè¿ããŸãïŒãåçŽãããã²ãŒã ã¯ãã¬ã€ã€ãŒã«ææŠããªãã®ã§ãé¢çœãã²ãŒã ã¯ããé£ããã¯ãã§ããäžæ¹ãè€éããæ³å€ãªå Žåã¯ãé åºãªããŒãã³ã¢ãã¬ãŒã€ãŒãŸãã¯èª°ãã«äœãã蚌æããããšããŠãã人ã ããã²ãŒã ã®èг客ããæ®ããŸããã²ãŒã ã¯éåžžã«å€æ§ã§ãããçæ³çã«ã¯-ç®æšãéæããããã®ããã€ãã®ãªãã·ã§ã³ãæäŸããåãã¬ã€ã€ãŒã奜ã¿ã®ãªãã·ã§ã³ãéžæã§ããããã«ããŸããäžã€ã®åæ ŒæŠç¥ãæ®ããæ¯é
ããã¹ãã§ã¯ãããŸãããããã§ãªããã°ã圌ãã¯ããã䜿çšããã ãã§ã...ãªã©ãèšãæããã°ãã²ãŒã ã®ãã©ã³ã¹ããšãå¿
èŠããããŸããããã¯ãã«ãŒã«ãæç¢ºã«åœ¢åŒåãããŠããããŒãã²ãŒã ã«ç¹ã«åœãŠã¯ãŸããŸããã©ããã£ãŠããã®ïŒ ããããªãã
æ°åŠã¢ãã«ãäœæã§ããæ°åŠè
ã®å人ãããªãå ŽåïŒç§ã¯ãããèŠãããšããããŸãïŒãããã«ã€ããŠäœãçè§£ããŠããªãïŒãããŠç§ãã¡ã¯çè§£ããŠããªãïŒå Žåãå¯äžã®æ¹æ³ã¯ ãã¬ã€ãã¹ãã®çŽæã«é Œãããšã§ããæåã«èªåã§ã²ãŒã ããã¬ã€ããŸããããªããç²ãããšã-ããªãã®åŠ»ãæŒããããšãç³ãåºãŸããé¢å©åŸãä»ã®èŠªrelativeãå人ãç¥äººãè·¯äžã§ã©ã³ãã ãªäººã
ãšéã¶ããšãç³ãåºãŸããå®å
šã«äžäººã«ãªã£ãããã¢ã»ã³ããªãã€ã³ã¿ãŒãããã«ã¢ããããŒãããŸãã人ã
ã¯èå³ãæã¡ãéã³ãããšæãã§ãããããããŠããªãã¯åœŒãã«çããŸãïŒãããªãããã®ãã£ãŒãããã¯ïŒèª°ããããªããšåãããã«ããªãã®å€¢ãæããããªããšäžç·ã«åããããããããŸãã-ããªãã¯å¿ãåãããã人ã
ãŸãã¯å°ãªããšããµããŒãã°ã«ãŒããèŠã€ããã§ãããïŒãªãç§ã¯ãã®èšäºãæžãããšæããŸããïŒïŒïŒHeheïŒãåè«ã¯ãšããããç§ãã¡ã«...ãã¹ãŠã®çããã®æåãé¡ã£ãŠããŸããç¶ããèªãïŒã ããèããã§ãããïŒïŒ-ã²ãŒã ã®ãã¶ã€ã³ãªã©ã«ã€ããŠãç§ãã¡ã調ã¹ããã¹ãŠã®åé¡ã¯ããã§ã«äœããã®åœ¢ã§èšäºãæç®ã§åãäžããããŠããŸãïŒãã ãããŸã ããã«ããå Žåã¯ãèªãããã«ä¿ãããšã¯æããã«äžèŠã§ãïŒãããªãã®å°è±¡ãå
±æãããã©ãŒã©ã ã§ã³ãã¥ãã±ãŒã·ã§ã³ãåããŸããã-äžè¬ã«ãããªãã¯ãã§ã«ç§ãããè¯ãç¥ã£ãŠããŸããæ ããŠã¯ãããŸãããããªãã¯æåããŸãããã®æ¥œèгçãªã¡ã¢ã§ãããªãã®äŒæãåãããŠãã ããããæž
èŽããããšãããããŸãããããããïŒ
ããã£ïŒã©ã£ã¡ãèŠãŸããïŒããããã¹ãŠæºåž¯é»è©±ã§èµ·åããæ¹æ³ã¯ïŒç¡é§ã«åŸ
ã£ãŠããŸãããããããšãäœã§ããïŒãããšãããAndroid
ã²ãŒã ãšã³ãžã³ãšAndroidãã©ãããã©ãŒã ã®çµ±åã«ã€ããŠèª¬æããããã«ãã¯ã©ã¹ããã®ãŸãŸã«ãGame
ãŠãåæ§ã®ããããã¯ããã«åçŽãªã¯ã©ã¹ãèããŠã¿ãŸãããMainMenu
ãååã瀺ãããã«ãã¢ããªã±ãŒã·ã§ã³ã®ã¡ã€ã³ã¡ãã¥ãŒãå®è£
ããããšãç®çãšããŠãããå®éããŠãŒã¶ãŒã察話ãéå§ããæåã®ã¯ã©ã¹ã§ããã³ã³ãœãŒã«ã€ã³ã¿ãŒãã§ã€ã¹ã§ã¯ã次ã®ããã«ãªããŸã classã®ããã«ãGame
ç¡éã«ãŒããå®çŸ©ããŸããåå埩ã§ç»é¢ãæç»ããããŠãŒã¶ãŒããã³ãã³ããèŠæ±ãããŸããããã«ã¯è€éãªããžãã¯ã¯ãªãããããã®ã³ãã³ãã¯ã¯ããã«å°ãããªã£ãŠããŸããåºæ¬çã«1ã€ã®ããšãå®è£
ããŠããŸã-ãçµäºããã¡ã€ã³ã¡ãã¥ãŒã®ã¢ã¯ãã£ããã£ãã£ãŒã ç°¡åã§ããã ãããšã¹ããŒãã«ã€ããŠã
ã³ãŒããäžæ¡åçŽã§ãã class MainMenu( private val renderer: MenuRenderer, private val interactor: MenuInteractor ) { private var actions = ActionList.EMPTY fun start() { Audio.playMusic(Music.MENU_MAIN) actions = ActionList() actions.add(Action.Type.NEW_ADVENTURE) actions.add(Action.Type.CONTINUE_ADVENTURE, false) actions.add(Action.Type.MANUAL, false) actions.add(Action.Type.EXIT) processCycle() } private fun processCycle() { while (true) { renderer.drawMainMenu(actions) when (interactor.pickAction(actions).type) { Action.Type.NEW_ADVENTURE -> TODO() Action.Type.CONTINUE_ADVENTURE -> TODO() Action.Type.MANUAL -> TODO() Action.Type.EXIT -> { Audio.stopMusic() Audio.playSound(Sound.LEAVE) renderer.clearScreen() Thread.sleep(500) return } else -> throw AssertionError("Should not happen") } } } }
ãŠãŒã¶ãŒãšã®å¯Ÿè©±ã¯ã以åã«èŠããããã®ãšåæ§ã«æ©èœããã€ã³ã¿ãŒãã§ãŒã¹MenuRenderer
ãšã䜿çšããŠå®è£
ãããŸãMenuInteractor
ã interface MenuRenderer: Renderer { fun drawMainMenu(actions: ActionList) } interface Interactor { fun anyInput() fun pickAction(list: ActionList): Action }
æ¢ã«çè§£ããŠããããã«ãã€ã³ã¿ãŒãã§ã€ã¹ãç¹å®ã®å®è£
ããæå³çã«åé¢ããŸãããå¿
èŠãªã®ã¯ãCliãããžã§ã¯ããæ°ãããããžã§ã¯ãã«çœ®ãæãïŒDroidãšåŒã³ãŸãããïŒãCoreãããžã§ã¯ãã«äŸåé¢ä¿ã远å ããããšã§ãããã£ãŠã¿ãŸããããAndroid Studioãå®è¡ãïŒéåžžã¯Androidåãã®ãããžã§ã¯ããéçºãããŸãïŒãåçŽãªãããžã§ã¯ããäœæããäžèŠãªæšæºèŠæãåãããã¹ãŠåé€ããKotlinèšèªã®ãµããŒãã®ã¿ãæ®ããŸãããŸããCoreãããžã§ã¯ããžã®äŸåé¢ä¿ã远å ããŸããããã¯ããã·ã³ã®ããŒã«ã«Mavenãªããžããªã«ä¿åãããŸãã apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 28 defaultConfig { applicationId "my.company.dice" minSdkVersion 14 targetSdkVersion 28 versionCode 1 versionName "1.0" } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "my.company:dice-core:1.0" }
ãã ããããã©ã«ãã§ã¯ã誰ãäŸåé¢ä¿ãèªèããŸããããããžã§ã¯ãã®ãã«ãæã«ããŒã«ã«ãªããžããªïŒmavenLocalïŒã䜿çšããå¿
èŠãããããšãæç€ºçã«ç€ºãå¿
èŠããããŸãã buildscript { ext.kotlin_version = '1.3.20' repositories { google() jcenter() mavenLocal() } dependencies { classpath 'com.android.tools.build:gradle:3.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { google() jcenter() mavenLocal() } }
以åã«éçºããããã¹ãŠã®ã¯ã©ã¹ã䜿çšã®ããã«ã¢ã¯ã»ã¹å¯èœã§ãããå®è£
ã®ããã®ã€ã³ã¿ãŒãã§ãŒã¹ã§ããããšãããããŸããç§ãã¡ã¯ãã«èå³ããããããšã§ãå€§èŠæš¡ãæã
ã¯ãã§ã«ããªãã¿ã®ã€ã³ã¿ãŒãã§ã€ã¹ã§ãïŒSoundPlayer
ãMusicPlayer
ãMenuInteractor
ïŒã¢ããã°GameInteractor
ïŒMenuRenderer
ïŒã¢ããã°GameRenderer
ïŒããã³StringLoader
ãã®ããç§ã¯ãAndroidã®å®è£
ã«æ°ãããç¹å®ãæžããŸãããã ãããã®åã«ããŠãŒã¶ãŒãæ°ããã·ã¹ãã ãšã©ã®ããã«çžäºäœçšããããäžè¬çã«ææ¡ããŸããã€ã³ã¿ãŒãã§ã€ã¹èŠçŽ ã®ã¬ã³ããªã³ã°ã«ã¯ãAndroidã®æšæºã³ã³ããŒãã³ãïŒãã¿ã³ãç»åãå
¥åãã£ãŒã«ããªã©ïŒã¯äœ¿çšããŸããã代ããã«ãã¯ã©ã¹ã®æ©èœã«å¶éãCanvas
ãŸãããããè¡ãã«ã¯ãåäžã®ã¯ã©ã¹ã®åå«ãäœæããã ãã§ååã§ãView
-ãããããã£ã³ãã¹ãã«ãªããŸããå
¥åã®å ŽåãããŒããŒãã¯ãããªããããå°ãè€éã«ãªããŸããç»é¢ã®ç¹å®ã®éšåã§ã®ãŠãŒã¶ãŒå
¥åãã³ãã³ãã®å
¥åãšèŠãªãããããã«ã€ã³ã¿ãŒãã§ã€ã¹ãèšèšããå¿
èŠããããŸãããããè¡ãã«ã¯ãåãçžç¶äººã䜿çšããŸãView
-ãã®ããã«ããŠã圌ã¯ãŠãŒã¶ãŒãšã²ãŒã ãšã³ãžã³ã®éã®ä»²ä»è
ãšããŠæ©èœããŸãïŒã·ã¹ãã ã³ã³ãœãŒã«ããã®ãããªä»²ä»è
ãšããŠæ©èœããæ¹æ³ã«äŒŒãŠããŸãïŒããã¥ãŒã®ã¡ã€ã³ã¢ã¯ãã£ããã£ãäœæãããããã§ã¹ãã«æžã蟌ã¿ãŸãããã <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="my.company.dice"> <application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <activity android:name=".ui.MainActivity" android:screenOrientation="sensorLandscape" android:configChanges="orientation|keyboardHidden|screenSize"> <intent-filter> <category android:name="android.intent.category.LAUNCHER"/> <action android:name="android.intent.action.MAIN"/> </intent-filter> </activity> </application> </manifest>
ã¢ã¯ãã£ããã£ã暪åãã«ä¿®æ£ããŸã-ä»ã®ã»ãšãã©ã®ã²ãŒã ã®å Žåã®ããã«ãããŒãã¬ãŒããããŒãã¬ãŒãã«ããããšã¯ã§ããŸãããããã«ãããã€ã¹ã®ç»é¢å
šäœã«æ¡åŒµããããã«å¿ããŠã¡ã€ã³ããŒããåŠæ¹ããŸãã <resources> <style name="AppTheme" parent="android:Theme.Black.NoTitleBar.Fullscreen"/> </resources>
ãããŠããªãœãŒã¹ã«å
¥ã£ãã®ã§ãå¿
èŠãªããŒã«ã©ã€ãºãããæååãCliãããžã§ã¯ããã転éããç®çã®åœ¢åŒã«ããŸãã <resources> <string name="action_new_adventure_key">N</string> <string name="action_new_adventure_name">ew adventure</string> <string name="action_continue_adventure_key">C</string> <string name="action_continue_adventure_name">ontinue adventure</string> <string name="action_manual_key">M</string> <string name="action_manual_name">anual</string> <string name="action_exit_key">X</string> <string name="action_exit_name">Exit</string> </resources>
ã¡ã€ã³ã¡ãã¥ãŒã§äœ¿çšããããµãŠã³ããšé³æ¥œã®ãã¡ã€ã«ïŒåã¿ã€ãã®1ã€ïŒãšåæ§ã«ããããããããã/assets/sound/leave.wav
andã«é
眮ã/assets/music/menu_main.mp3
ãŸãããªãœãŒã¹ãçè§£ããããèšèšãè¡ããšãã§ããïŒã¯ãïŒãã³ã³ãœãŒã«ãšã¯ç°ãªããAndroidãã©ãããã©ãŒã ã«ã¯ç¬èªã®ã¢ãŒããã¯ãã£æ©èœããããç¹å®ã®ã¢ãããŒããšæ¹æ³ã䜿çšããå¿
èŠããããŸããã¯ã©ã¹ãšã€ã³ã¿ãŒãã§ã€ã¹å³ æ°ãã€ããªãã§ãã ãããä»ç§ã¯ãã¹ãŠã詳现ã«èª¬æããŸãããããããæãé£ããã¯ã©ã¹DiceSurface
- ã¯ã©ã¹- View
ã·ã¹ãã ã®ç¬ç«ããéšåãçµåããããã«åŒã³åºãããéåžžã«åŸç¶è
ããå§ããŸãïŒå¿
èŠã«å¿ããŠãã¯ã©ã¹ããç¶æ¿ããããšãã§ããŸãSurfaceView
-ãŸãã¯GlSurfaceView
-å¥ã®ã¹ã¬ããã§æç»ããããšãã§ããŸãããã¢ãã¡ãŒã·ã§ã³ããŒã¹ã®ã¿ãŒã³ããŒã¹ã®ã²ãŒã ããããŸããããã¯è€éãªã°ã©ãã£ãã¯åºåãå¿
èŠãšããªããããè€éã«ããŸããïŒãåè¿°ã®ããã«ããã®å®è£
ã«ãããç»ååºåãšã¯ãªãã¯åŠçã®2ã€ã®åé¡ãåæã«è§£æ±ºãããŸããããããã«äºæ³å€ã®åé¡ããããŸããããããé çªã«èããŠã¿ãŸããããã³ã³ãœãŒã«ã«ãã€ã³ããããšãã¬ã³ãã©ãŒã¯åºåã³ãã³ããéä¿¡ããç»é¢ã«ç»åã圢æããŸããã Androidã®å Žåãç¶æ³ã¯éã§ããã¬ã³ããªã³ã°ã¯Viewèªäœã«ãã£ãŠéå§ãããã¡ãœããonDraw()
ãå®è¡ããããŸã§ã«ãäœããã©ã®ããã«ãã©ãã«æç»ãããããã§ã«ç¥ã£ãŠããã¯ãã§ããããããdrawMainMenu()
ã€ã³ã¿ãŒãã§ãŒã¹ã¡ãœããã¯MainMenu
ã©ãã§ããïŒåœŒã¯ä»ãåºåãå¶åŸ¡ããŠããŸãããïŒæ©èœçãªã€ã³ã¿ãŒãã§ãŒã¹ã䜿çšããŠãã®åé¡ã解決ããŠã¿ãŸããããã¯ã©ã¹DiceSurface
ã«ã¯ç¹å¥ãªãã©ã¡ãŒã¿ãŒãå«ãŸããŸãinstructions
ãå®éã«ã¯ãã¡ãœãããåŒã³åºããããã³ã«å®è¡ããå¿
èŠãããã³ãŒãã®ãããã¯ã§ãonDraw()
ãã¬ã³ãã©ãŒã¯ããããªãã¯ã¡ãœããã䜿çšããŠãã©ã®ç¹å®ã®æç€ºã«åŸãã¹ããã瀺ããŸããèå³ã®ããæ¹ã¯ããšåŒã°ãããã¿ãŒã³ãäœ¿çšæŠç¥ïŒæŠç¥ïŒã次ã®ããã«ãªããŸãã
typealias RenderInstructions = (Canvas, Paint) -> Unit class DiceSurface(context: Context) : View(context) { private var instructions: RenderInstructions = { _, _ -> } private val paint = Paint().apply { color = Color.YELLOW style = Paint.Style.STROKE isAntiAlias = true } fun updateInstructions(instructions: RenderInstructions) { this.instructions = instructions this.postInvalidate() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawColor(Color.BLACK)
ã€ãŸãããã¹ãŠã®ã°ã©ãã£ã«ã«æ©èœã¯ãŸã Rendererã¯ã©ã¹ã«ãããŸãããä»åã¯ã³ãã³ããçŽæ¥å®è¡ããã®ã§ã¯ãªããViewã«ããå®è¡ã®æºåãããŸããããããã£ã®ã¿ã€ãã«æ³šæããŠinstructions
ãã ãã-å¥ã®ã€ã³ã¿ãŒãã§ã€ã¹ãäœæããŠãã®å¯äžã®ã¡ãœãããåŒã³åºãããšãã§ããŸãããKotlinã¯ã³ãŒãã®éã倧å¹
ã«åæžã§ããŸããæ¬¡ã«ãInteractorã«ã€ããŠèª¬æããŸãã以åã¯ãããŒã¿å
¥åã¯åæã§ãããã³ã³ãœãŒã«ïŒããŒããŒãïŒããããŒã¿ãèŠæ±ãããšããŠãŒã¶ãŒãããŒãæŒããŸã§ã¢ããªã±ãŒã·ã§ã³ïŒãµã€ã¯ã«ïŒãäžæãããŸããã Androidã§ã¯ããã®ãããªããªãã¯ã¯æ©èœããŸãããç¬èªã®Looperãããããã®æ©èœã¯äžæããããšã¯ãããŸãããã€ãŸããå
¥åã¯éåæã§ãªããã°ãªããŸãããã€ãŸããInteractorã€ã³ã¿ãŒãã§ãŒã¹ã¡ãœããã¯ãšã³ãžã³ãäžæåæ¢ããŠã³ãã³ããåŸ
æ©ããŸãããActivityãšãã®ãã¹ãŠã®ãã¥ãŒã¯é
ããæ©ãããã®ã³ãã³ããéä¿¡ãããŸã§æ©èœãç¶ããŸãããã®ã¢ãããŒãã¯ãæšæºã€ã³ã¿ãŒãã§ãŒã¹ã䜿çšããŠå®è£
ããã®ãéåžžã«ç°¡åBlockingQueue
ã§ããã¯ã©ã¹DroidMenuInteractor
ã¯ã¡ãœãããåŒã³åºããŸãtake()
ãèŠçŽ ïŒæ¢ç¥ã®ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹Action
ïŒããã¥ãŒã«è¡šç€ºããããŸã§ãã²ãŒã ã¹ããªãŒã ã®å®è¡ãäžæåæ¢ããŸããDiceSurface
ããŠãŒã¶ãŒã®ã¯ãªãã¯ã«åãããŠèª¿æŽãïŒæšæºonTouchEvent()
ã¯ã©ã¹ã¡ãœããView
ïŒããªããžã§ã¯ããçæããã¡ãœããã«ãã£ãŠãã¥ãŒã«è¿œå ããŸãoffer()
ãæ¬¡ã®ããã«ãªããŸãã class DiceSurface(context: Context) : View(context) { private val actionQueue: BlockingQueue<Action> = LinkedBlockingQueue<Action>() fun awaitAction(): Action = actionQueue.take() override fun onTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_UP) { actionQueue.offer(Action(Action.Type.NONE), 200, TimeUnit.MILLISECONDS) } return true } } class DroidMenuInteractor(private val surface: DiceSurface) : Interactor { override fun anyInput() { surface.awaitAction() } override fun pickAction(list: ActionList): Action { while (true) { val type = surface.awaitAction().type list .filter(Action::isEnabled) .find { it.type == type } ?.let { return it } } } }
ã€ãŸããInteractorã¯ã¡ãœããawaitAction()
ãåŒã³åºãããã¥ãŒã«äœããããã°ãåä¿¡ããã³ãã³ããåŠçããŸããããŒã ããã¥ãŒã«è¿œå ãããæ¹æ³ã«æ³šæããŠãã ããã UIã¹ããªãŒã ã¯é£ç¶ããŠå®è¡ãããããããŠãŒã¶ãŒã¯ç»é¢ãäœåºŠãé£ç¶ããŠã¯ãªãã¯ããããšãã§ããŸããããã¯ãç¹ã«ã²ãŒã ãšã³ãžã³ãã³ãã³ããåä¿¡ããæºåãã§ããŠããªãå ŽåïŒããšãã°ãã¢ãã¡ãŒã·ã§ã³äžïŒã«ãã³ã°ã¢ããããå¯èœæ§ããããŸããã®å Žåããã¥ãŒã®å®¹éãå¢ããããã¿ã€ã ã¢ãŠãå€ãæžãããããŸãã¯ãã®äž¡æ¹ã圹ç«ã¡ãŸãããã¡ãããã³ãã³ãã転éããŸãããããã¯å¯äžã®ãã®ã§ããæŒã座æšãåºå¥ããå¿
èŠãããããããã®å€ã«å¿ããŠããã®ã³ãã³ããŸãã¯ãã®ã³ãã³ããåŒã³åºããŸãããã ããããã¯äžéã§ã-Interactorã¯ãã¢ã¯ãã£ããªãã¿ã³ãç»é¢äžã®ã©ã®å Žæã«æç»ããããããããŸãã-ã¬ã³ãã©ãŒãã¬ã³ããªã³ã°ãæ
åœããŸããæ¬¡ã®ããã«çžäºäœçšã確ç«ããŸãããã®ã¯ã©ã¹DiceSurface
ã¯ãç¹å¥ãªã³ã¬ã¯ã·ã§ã³-ã¢ã¯ãã£ããªé·æ¹åœ¢ïŒãŸãã¯ãã®ãã€ã³ãã«å°éããå Žåã¯ä»ã®åœ¢ç¶ïŒã®ãªã¹ããæ ŒçŽããŸãããã®ãããªé·æ¹åœ¢ã«ã¯ãé ç¹ã®åº§æšãšå¢çã®åº§æšãå«ãŸããŸãAction
ãã¬ã³ãã©ãŒã¯ãããã®é·æ¹åœ¢ãçæããŠãªã¹ãã«è¿œå ããã¡ãœããonTouchEvent()
ã¯ã©ã®é·æ¹åœ¢ãæŒããããã倿ãã察å¿ããé·æ¹åœ¢ããã¥ãŒã«è¿œå ããŸãAction
ã private class ActiveRect(val action: Action, left: Float, top: Float, right: Float, bottom: Float) { val rect = RectF(left, top, right, bottom) fun check(x: Float, y: Float, w: Float, h: Float) = rect.contains(x / w, y / h) }
ãã®ã¡ãœããcheck()
ã¯ãæå®ããã座æšãé·æ¹åœ¢ã®å
åŽã«ãããã©ããã確èªããŸããã¬ã³ãã©ãŒã®äœæ¥æ®µéã§ã¯ïŒããããŸãã«é·æ¹åœ¢ãäœæãããç¬éã§ãïŒããã£ã³ãã¹ã®ãµã€ãºã«ã€ããŠã¯ããããªãããšã«æ³šæããŠãã ããããããã£ãŠã座æšãçžå¯Ÿå€ïŒç»é¢ã®å¹
ãŸãã¯é«ãã®å²åïŒã§0ã1ã®å€ã§ä¿åããæŒããšãã«åã«ãŠã³ãããå¿
èŠããããŸãããã®ã¢ãããŒãã¯ãã¢ã¹ãã¯ãæ¯ãèæ
®ã«å
¥ããŠããªããããå®å
šã«æ£ç¢ºã§ã¯ãããŸãããå°æ¥çã«ã¯ãããçŽãå¿
èŠããããŸãããã ããæåã®æè²çãªã¿ã¹ã¯ã«ã€ããŠã¯ããã§ååã§ããã¯ã©ã¹DiceSurface
ã«è¿œå ã®ãã£ãŒã«ããå®è£
ãã2ã€ã®ã¡ãœããïŒaddRectangle()
ããã³clearRectangles()
ïŒã远å ããŠå€éšããïŒã¬ã³ãã©ãŒåŽããïŒå¶åŸ¡ãonTouchEvent()
ãé·æ¹åœ¢ã®åº§æšãèæ
®ããŠæ¡åŒµããŸãã class DiceSurface(context: Context) : View(context) { private val actionQueue: BlockingQueue<Action> = LinkedBlockingQueue<Action>() private val rectangles: MutableSet<ActiveRect> = Collections.newSetFromMap(ConcurrentHashMap<ActiveRect, Boolean>()) private var instructions: RenderInstructions = { _, _ -> } private val paint = Paint().apply { color = Color.YELLOW style = Paint.Style.STROKE isAntiAlias = true } fun updateInstructions(instructions: RenderInstructions) { this.instructions = instructions this.postInvalidate() } fun clearRectangles() { rectangles.clear() } fun addRectangle(action: Action, left: Float, top: Float, right: Float, bottom: Float) { rectangles.add(ActiveRect(action, left, top, right, bottom)) } fun awaitAction(): Action = actionQueue.take() override fun onTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_UP) { with(rectangles.firstOrNull { it.check(event.x, event.y, width.toFloat(), height.toFloat()) }) { if (this != null) { actionQueue.put(action) } else { actionQueue.offer(Action(Action.Type.NONE), 200, TimeUnit.MILLISECONDS) } } } return true } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawColor(Color.BLACK) instructions(canvas, paint) } }
ç«¶åããã³ã¬ã¯ã·ã§ã³ã䜿çšããŠåè§åœ¢ãä¿åããŸã- ConcurrentModificationException
ç°ãªãã¹ã¬ããã«ãã£ãŠã»ãããåæã«æŽæ°ããã³ç§»åãããå Žåã«çºçãåé¿ã§ããŸãïŒãã®å Žåã¯çºçããŸãïŒãã¯ã©ã¹ã³ãŒãDroidMenuInteractor
ã¯å€æŽãããŸããããDroidMenuRenderer
倿ŽãããŸããåé
ç®ã®è¡šç€ºã«4ã€ã®ãã¿ã³ã远å ããŸãActionList
ãç»é¢ã®å¹
å
šäœã«åçã«é
眮ãããèŠåºãDICEã®äžã«é
眮ããŸããããŠãã¢ã¯ãã£ããªé·æ¹åœ¢ãå¿ããªãã§ãã ããã class DroidMenuRenderer ( private val surface: DiceSurface, private val loader: StringLoader ) : MenuRenderer { protected val helper = StringLoadHelper(loader) override fun clearScreen() { surface.clearRectangles() surface.updateInstructions { _, _ -> } } override fun drawMainMenu(actions: ActionList) {
ããStringLoader
ã§ããã«ããŒã¯ã©ã¹ã®ã€ã³ã¿ãŒãã§ã€ã¹ãšæ©èœã«æ»ããŸãStringLoadHelper
ïŒå³ã«ã¯ç€ºãããŠããŸããïŒãæåã®å®è£
ã«ã¯ååãResourceStringLoader
ãããïŒæããã«ïŒã¢ããªã±ãŒã·ã§ã³ãªãœãŒã¹ããããŒã«ã©ã€ãºãããæååãããŒãããŸãããã ãããªãœãŒã¹èå¥åãäºåã«ããããªããããããã¯åçã«è¡ãããŸããå€åºå
ã§ãªãœãŒã¹èå¥åãäœæããå¿
èŠããããŸãã class ResourceStringLoader(context: Context) : StringLoader { private val packageName = context.packageName private val resources = context.resources override fun loadString(key: String): String = resources.getString(resources.getIdentifier(key, "string", packageName)) }
é³ãšé³æ¥œã«ã€ããŠè©±ãããšã¯æ®ã£ãŠããŸããAndroid MediaPlayer
ã«ã¯ããããã®ããšãæ±ãçŽ æŽãããã¯ã©ã¹ããããŸãã鳿¥œãæŒå¥ããããã®è¯ããã®ã¯ãããŸããïŒ class DroidMusicPlayer(private val context: Context): MusicPlayer { private var currentMusic: Music? = null private val player = MediaPlayer() override fun play(music: Music) { if (currentMusic == music) { return } currentMusic = music player.setAudioStreamType(AudioManager.STREAM_MUSIC) val afd = context.assets.openFd("music/${music.toString().toLowerCase()}.mp3") player.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length) player.setOnCompletionListener { it.seekTo(0) it.start() } player.prepare() player.start() } override fun stop() { currentMusic = null player.release() } }
2ã€ã®ãã€ã³ãããŸããã¡ãœããprepare()
ã¯åæçã«å®è¡ãããŸããããã¯ããã¡ã€ã«ãµã€ãºã倧ããå ŽåïŒãããã¡ãªã³ã°ã®ããïŒãã·ã¹ãã ãäžæããŸããå¥ã®ã¹ã¬ããã§å®è¡ããããéåæã¡ãœããprepareAsync()
ãšã䜿çšããããšããå§ãããŸãOnPreparedListener
ã第äºã«ãåçãã¢ã¯ãã£ããã£ã©ã€ããµã€ã¯ã«ïŒãŠãŒã¶ãŒãã¢ããªã±ãŒã·ã§ã³ãæå°åãããšäžæåæ¢ããå埩ãããšåéããïŒãé¢é£ä»ãããšäŸ¿å©ã§ãããããããŸããã§ããã Ai-ai-ai ...ãµãŠã³ãMediaPlayer
ã«ãé©ããŠããŸãããæ°ãå°ãªãã·ã³ãã«ãªå ŽåïŒãã®å Žåã®ããã«ïŒãããã§ååã§ãSoundPool
ããã®å©ç¹ã¯ããµãŠã³ããã¡ã€ã«ãæ¢ã«ã¡ã¢ãªã«ããŒããããŠããå Žåããã®åçãå³åº§ã«éå§ãããããšã§ããæ¬ ç¹ã¯æããã§ã-ååãªã¡ã¢ãªããªãå¯èœæ§ããããŸãïŒããããç§ãã¡ã«ãšã£ãŠã¯ååã§ãããæ§ããã§ãïŒã class DroidSoundPlayer(context: Context) : SoundPlayer { private val soundPool: SoundPool = SoundPool(2, AudioManager.STREAM_MUSIC, 100) private val sounds = mutableMapOf<Sound, Int>() private val rate = 1f private val lock = ReentrantReadWriteLock() init { Thread(SoundLoader(context)).start() } override fun play(sound: Sound) { if (lock.readLock().tryLock()) { try { sounds[sound]?.let { s -> soundPool.play(s, 1f, 1f, 1, 0, rate) } } finally { lock.readLock().unlock() } } } private inner class SoundLoader(private val context: Context) : Runnable { override fun run() { val assets = context.assets lock.writeLock().lock() try { Sound.values().forEach { s -> sounds[s] = soundPool.load( assets.openFd("sound/${s.toString().toLowerCase()}.wav"), 1 ) } } finally { lock.writeLock().unlock() } } } }
ã¯ã©ã¹ãäœæãããšããåæããã®ãã¹ãŠã®ãµãŠã³ãSound
ã¯å¥ã®ã¹ããªãŒã ã§ãªããžããªã«ããŒããããŸããä»åã¯åæã³ã¬ã¯ã·ã§ã³ã䜿çšããŸããããæšæºã¯ã©ã¹ã䜿çšããŠãã¥ãŒããã¯ã¹ãå®è£
ãReentrantReadWriteLock
ãŸããããŠãæåŸã«ãç§ãã¡MainActivity
ã¯å
éšã§ãã¹ãŠã®ã³ã³ããŒãã³ããäžç·ã«ãã©ã€ã³ãããŸã-ãããå¿ããŠããŸãããïŒMainMenu
ïŒããã³Game
ãã®åŸïŒå¥ã®ã¹ã¬ããã§èµ·åããå¿
èŠãããããšã«æ³šæããŠãã ããã class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Audio.init(DroidSoundPlayer(this), DroidMusicPlayer(this)) val surface = DiceSurface(this) val renderer = DroidMenuRenderer(surface) val interactor = DroidMenuInteractor(surface, ResourceStringLoader(this)) setContentView(surface) Thread { MainMenu(renderer, interactor).start() finish() }.start() } override fun onBackPressed() { } }
å®éãããããã¹ãŠã§ãã
ãã¹ãŠã®èŠçã®åŸãç§ãã¡ã®ã¢ããªã±ãŒã·ã§ã³ã®ã¡ã€ã³ç»é¢ã¯åçŽã«é©ãã»ã©ã«èŠããŸãïŒã¢ãã€ã«ç»é¢å
šäœã®ã¡ã€ã³ã¡ãã¥ãŒ ãŸããã€ãŸããç¥çãªã¢ãŒãã£ã¹ããç§ãã¡ã®ã©ã³ã¯ã«ç»å Žãããšããããã¯é©ãã»ã©ã«èŠããŸãããããŠã圌ã®å©ãã§ãã®ã¹ã«ã©ãŒã¯å®å
šã«åæç»ãããŸãã䟿å©ãªãªã³ã¯
ç§ã¯ç¥ã£ãŠãããå€ãã¯ãã®ãã€ã³ãã«ãŸã£ããã«ã¹ã¯ããŒã«ããŸããã倧äžå€«ã§ã-ã»ãšãã©ã®èªè
ã¯ã¿ããå®å
šã«éããŠããŸããããã«ããããããããã®äžè²«æ§ã®ãªããããã¹ãã®æµããã¹ãŠã«èãããŠããã- å°æ¬ãšå°æ¬ãç¡éã®æãšæè¬ããŸããšãªã³ã¯ããã¡ãããããããªãã§ããŸãããããžã§ã¯ãã®ãœãŒã¹ã³ãŒãïŒãããžã§ã¯ãã®çŸåšã®ç¶æ
ã¯ãèšäºã§æ€èšãããŠããç¶æ
ãããã¯ããã«é²ãã§ããããšã«æ³šæããŠãã ããïŒïŒããŠãçªç¶èª°ãããããžã§ã¯ããéå§ããŠèŠãããšæãããã«ãªããèªåã§æ inessãéããããã«ãããã«äœæ¥ããŒãžã§ã³ãžã®ãªã³ã¯ããããŸãïŒLINKïŒããã§ã¯ã䟿å©ãªã©ã³ãã£ãŒã䜿çšããŠèµ·åããŸãïŒäœæã«ã€ããŠå¥ã®èšäºãæžãããšãã§ããŸãïŒã JavaFXã䜿çšãããããOpenJDKïŒæžã蟌ã¿ããã³ãã«ãïŒãæèŒãããã·ã³ã§ã¯èµ·åããªãå ŽåããããŸãããå°ãªããšããã¡ã€ã«ãã¹ãæåã§ç»é²ããå¿
èŠã¯ãããŸãããã€ã³ã¹ããŒã«ã®ãã«ãã¯readme.txtãã¡ã€ã«ã«å«ãŸããŠããŸãïŒèŠããŠããŸããïŒïŒãããŠã³ããŒãããŠãèŠãŠã䜿çšããŠãæåŸã«ç§ã¯é»ã£ãŠããŸãããããžã§ã¯ãã䜿çšããããŒã«ãã¡ã«ããã¯ããŸãã¯è峿·±ããœãªã¥ãŒã·ã§ã³ã«èå³ãããå ŽåããŸãã¯ã²ãŒã ãããããªãå Žåã¯ãå¥ã®èšäºã§è©³çްã調ã¹ãããšãã§ããŸããå¿
èŠã«å¿ããŠãå¿
èŠãªãå Žåã¯ãã³ã¡ã³ããåŸæãææ¡ãéä¿¡ããŠãã ããã話ãããã§ãããã¹ãŠæé«ã