@@ -4,7 +4,7 @@ import { GameBase, IAPGameState, IClickResult, IIndividualState, IRenderOpts, IV
44import { APGamesInformation } from "../schemas/gameinfo" ;
55import { APRenderRep , MarkerDots , MarkerGlyph } from "@abstractplay/renderer/src/schemas/schema" ;
66import { APMoveResult } from "../schemas/moveresults" ;
7- import { reviver , UserFacingError } from "../common" ;
7+ import { reviver , shuffle , UserFacingError } from "../common" ;
88import { connectedComponents } from 'graphology-components' ;
99import i18next from "i18next" ;
1010import { HexTriGraph } from "../common/graphs" ;
@@ -24,8 +24,38 @@ interface IMoveState extends IIndividualState {
2424export interface IOmnyState extends IAPGameState {
2525 winner : playerid [ ] ;
2626 stack : Array < IMoveState > ;
27+ startStars ?: string [ ] ;
2728} ;
2829
30+ // ensures random stars have a certain minimum distance from each other
31+ const starsValid = ( g : HexTriGraph , stars : string [ ] ) : boolean => {
32+ for ( const star of stars ) {
33+ const [ sx , sy ] = g . algebraic2coords ( star ) ;
34+ // immediate neighbours
35+ const n1 = new Set < string > ( g . neighbours ( star ) ) ;
36+ // neighbours of neighbours
37+ const n2 = new Set < string > ( ) ;
38+ [ ...n1 ] . forEach ( cell => g . neighbours ( cell ) . forEach ( n => n2 . add ( n ) ) ) ;
39+ // combined
40+ const alln = new Set < string > ( [ ...n1 , ...n2 ] ) ;
41+ // delete starting cell
42+ alln . delete ( star ) ;
43+ // delete each cell at straight-line distance 2 away
44+ for ( const dir of HexTriGraph . directions ) {
45+ const ray = g . ray ( sx , sy , dir ) . map ( c => g . coords2algebraic ( ...c ) ) ;
46+ if ( ray . length >= 2 ) {
47+ alln . delete ( ray [ 1 ] ) ;
48+ }
49+ }
50+ // if any of the stars appear in this set, it's invalid
51+ if ( stars . filter ( s => alln . has ( s ) ) . length > 0 ) {
52+ return false ;
53+ }
54+ }
55+ // if we get to this point, it's valid
56+ return true ;
57+ }
58+
2959export class OmnyGame extends GameBase {
3060 public static readonly gameinfo : APGamesInformation = {
3161 name : "Omny" ,
@@ -50,7 +80,12 @@ export class OmnyGame extends GameBase {
5080 { uid : "size-10" , group : "board" } ,
5181 { uid : "constellation" , group : "stars" } ,
5282 { uid : "gyre" , group : "stars" } ,
83+ { uid : "yex" , group : "stars" } ,
5384 { uid : "free" , group : "stars" } ,
85+ { uid : "random-3" , group : "stars" } ,
86+ { uid : "random-5" , group : "stars" } ,
87+ { uid : "random-7" , group : "stars" } ,
88+ { uid : "random-9" , group : "stars" } ,
5489 { uid : "captures" } ,
5590 ] ,
5691 categories : [ "goal>connect" , "mechanic>place" , "mechanic>stack" , "mechanic>move" , "mechanic>coopt" , "board>shape>hex" , "board>connect>hex" , "components>simple>1per" ] ,
@@ -66,6 +101,7 @@ export class OmnyGame extends GameBase {
66101 public results : Array < APMoveResult > = [ ] ;
67102 public variants : string [ ] = [ ] ;
68103 private tmpstars = new Set < string > ( ) ;
104+ public startStars ?: string [ ] ;
69105
70106 constructor ( state ?: IOmnyState | string , variants ?: string [ ] ) {
71107 super ( ) ;
@@ -80,10 +116,28 @@ export class OmnyGame extends GameBase {
80116 this . variants = [ ...state . variants ] ;
81117 this . winner = [ ...state . winner ] ;
82118 this . stack = [ ...state . stack ] ;
119+ if ( state . startStars !== undefined ) {
120+ this . startStars = [ ...state . startStars ] ;
121+ }
83122 } else {
84123 if ( ( variants !== undefined ) && ( variants . length > 0 ) ) {
85124 this . variants = [ ...variants ] ;
86125 }
126+
127+ const found = this . variants . find ( v => v . startsWith ( "random" ) ) ;
128+ if ( found !== undefined ) {
129+ const [ , numStr ] = found . split ( "-" ) ;
130+ const num = parseInt ( numStr , 10 ) ;
131+ const g = this . graph ;
132+ let shuffled = shuffle ( g . graph . nodes ( ) ) as string [ ] ;
133+ let stars = shuffled . slice ( 0 , num ) ;
134+ while ( ! starsValid ( g , stars ) ) {
135+ shuffled = shuffle ( g . graph . nodes ( ) ) as string [ ] ;
136+ stars = shuffled . slice ( 0 , num ) ;
137+ }
138+ this . startStars = [ ...stars ] ;
139+ }
140+
87141 const board = new Map < string , playerid [ ] > ( ) ;
88142 const fresh : IMoveState = {
89143 _version : OmnyGame . gameinfo . version ,
@@ -166,6 +220,16 @@ export class OmnyGame extends GameBase {
166220 [ ...g . getEdges ( ) . values ( ) ] . forEach ( edge => edge . forEach ( cell => set . add ( cell ) ) ) ;
167221 set . add ( g . coords2algebraic ( size - 1 , size - 1 ) ) ;
168222 }
223+ // alternating corners
224+ else if ( this . variants . includes ( "yex" ) ) {
225+ set . add ( g . coords2algebraic ( 0 , 0 ) ) ;
226+ set . add ( g . coords2algebraic ( ( size * 2 ) - 2 , size - 1 ) ) ;
227+ set . add ( g . coords2algebraic ( 0 , ( size * 2 ) - 2 ) ) ;
228+ }
229+ // random start
230+ else if ( this . startStars !== undefined ) {
231+ this . startStars . forEach ( cell => set . add ( cell ) ) ;
232+ }
169233 // default Sunder (all cells)
170234 else {
171235 g . graph . nodes ( ) . forEach ( cell => set . add ( cell ) ) ;
@@ -585,6 +649,7 @@ export class OmnyGame extends GameBase {
585649 gameover : this . gameover ,
586650 winner : [ ...this . winner ] ,
587651 stack : [ ...this . stack ] ,
652+ startStars : this . startStars !== undefined ? [ ...this . startStars ] : undefined ,
588653 } ;
589654 }
590655
0 commit comments