11const { floor, random, min, max, hypot } = Math ;
22const assets : HTMLImageElement [ ] = [ ] ;
33
4+ type Ctx = CanvasRenderingContext2D ;
45type Tile = {
56 asset : HTMLImageElement ;
67 clickable ?: boolean ;
@@ -18,7 +19,6 @@ type BoardTile = Tile & {
1819let difficulty : 'easy' | 'hard' = 'easy' ;
1920const trough : Tile [ ] = [ ] ;
2021const board : BoardTile [ ] = [ ] ;
21- const mouse = { x : 0 , y : 0 , clicked : false } ;
2222
2323const boardLength = 8 ;
2424const tileWidth = 64 ;
@@ -40,6 +40,65 @@ const shuffle = <T>(xs: T[], k = 0) => {
4040 }
4141} ;
4242
43+ const handlePointer = ( ctx : Ctx , click : boolean ) => ( e : PointerEvent ) => {
44+ //TODO: this could be done only once the board changes
45+ for ( const tile of board ) {
46+ const topBlocked = board . some (
47+ other =>
48+ other . z === tile . z + 1 &&
49+ other . x >= tile . x - 1 &&
50+ other . x <= tile . x + 1 &&
51+ other . y >= tile . y - 1 &&
52+ other . y <= tile . y + 1 ,
53+ ) ;
54+ const neighbours = board . filter (
55+ other =>
56+ other != tile &&
57+ other . z === tile . z &&
58+ other . y === tile . y &&
59+ ( other . x + 2 === tile . x || other . x - 2 === tile . x ) ,
60+ ) ;
61+ tile . clickable = ! topBlocked && neighbours . length < 2 ;
62+ }
63+
64+ const rect = ctx . canvas . getBoundingClientRect ( ) ;
65+ const x = ( e . clientX - rect . left ) * ( ctx . canvas . width / rect . width ) ;
66+ const y = ( e . clientY - rect . top ) * ( ctx . canvas . height / rect . height ) ;
67+ //Figure out which tile is under the mouse
68+ const underMouse = board
69+ . filter ( x => x . clickable )
70+ . reduce (
71+ ( under , tile ) => {
72+ tile . highlight = false ;
73+ const isUnder =
74+ x >= tile . clientLeft &&
75+ x <= tile . clientRight &&
76+ y >= tile . clientTop &&
77+ y <= tile . clientBottom ;
78+ if ( ! isUnder ) return under ;
79+ if ( under ) return tile . z > under . z ? tile : under ;
80+ return tile ;
81+ } ,
82+ null as BoardTile | null ,
83+ ) ;
84+ if ( underMouse ) underMouse . highlight = true ;
85+
86+ if ( click && underMouse ?. clickable ) {
87+ const { asset } = underMouse ;
88+ const boardIdx = board . indexOf ( underMouse ) ;
89+ const troughIdx = trough . findIndex ( t => t . asset . src === asset . src ) ;
90+ if ( troughIdx === - 1 ) {
91+ if ( trough . length === ( difficulty ? 4 : 3 ) ) {
92+ return ;
93+ }
94+ trough . push ( { asset } ) ;
95+ } else {
96+ trough . splice ( troughIdx , 1 ) ;
97+ }
98+ board . splice ( boardIdx , 1 ) ;
99+ }
100+ } ;
101+
43102export const InitGame = async ( ctx : CanvasRenderingContext2D ) => {
44103 const orderedAssets = [ ...document . querySelectorAll ( 'img' ) ] ;
45104 shuffle ( orderedAssets ) ;
@@ -95,15 +154,8 @@ export const InitGame = async (ctx: CanvasRenderingContext2D) => {
95154 }
96155 }
97156
98- ctx . canvas . addEventListener ( 'pointermove' , e => {
99- const rect = ctx . canvas . getBoundingClientRect ( ) ;
100- mouse . x = ( e . clientX - rect . left ) * ( ctx . canvas . width / rect . width ) ;
101- mouse . y = ( e . clientY - rect . top ) * ( ctx . canvas . height / rect . height ) ;
102- } ) ;
103- ctx . canvas . addEventListener ( 'pointerdown' , ( ) => {
104- mouse . clicked = true ;
105- } ) ;
106- CalcClickable ( ) ;
157+ ctx . canvas . addEventListener ( 'pointermove' , handlePointer ( ctx , false ) ) ;
158+ ctx . canvas . addEventListener ( 'pointerdown' , handlePointer ( ctx , true ) ) ;
107159} ;
108160
109161const RenderTile = (
@@ -184,52 +236,8 @@ const RenderTile = (
184236 ctx . restore ( ) ;
185237} ;
186238
187- const CalcClickable = ( ) => {
188- for ( const tile of board ) {
189- const topBlocked = board . some (
190- other =>
191- other . z === tile . z + 1 &&
192- other . x >= tile . x - 1 &&
193- other . x <= tile . x + 1 &&
194- other . y >= tile . y - 1 &&
195- other . y <= tile . y + 1 ,
196- ) ;
197- const neighbours = board . filter (
198- other =>
199- other != tile &&
200- other . z === tile . z &&
201- other . y === tile . y &&
202- ( other . x + 2 === tile . x || other . x - 2 === tile . x ) ,
203- ) ;
204- tile . clickable = ! topBlocked && neighbours . length < 2 ;
205- }
206- } ;
207-
208- const HandleClick = ( ) => {
209- const clicked = board . find ( tile => tile . highlight ) ;
210- if ( ! clicked ?. clickable ) return ;
211- const boardIdx = board . indexOf ( clicked ) ;
212- const troughIdx = trough . findIndex ( t => t . asset . src === clicked . asset . src ) ;
213- if ( troughIdx === - 1 ) {
214- if ( trough . length === ( difficulty ? 4 : 3 ) ) {
215- return ;
216- }
217- trough . push ( { asset : clicked . asset } ) ;
218- } else {
219- trough . splice ( troughIdx , 1 ) ;
220- }
221- board . splice ( boardIdx , 1 ) ;
222- } ;
223-
224239let introduction = 0 ;
225240export const Render = ( ctx : CanvasRenderingContext2D ) => {
226- //Handle click
227- if ( mouse . clicked ) {
228- mouse . clicked = false ;
229- HandleClick ( ) ;
230- CalcClickable ( ) ;
231- }
232-
233241 ctx . fillStyle = '#132a3d' ;
234242 ctx . fillRect ( 0 , 0 , ctx . canvas . width , ctx . canvas . height ) ;
235243
@@ -266,25 +274,6 @@ export const Render = (ctx: CanvasRenderingContext2D) => {
266274 }
267275 ctx . translate ( margin , margin * 2 + troughHeight ) ;
268276
269- //Figure out which tile is under the mouse
270- const underMouse = board
271- . filter ( x => x . clickable )
272- . reduce (
273- ( under , tile ) => {
274- tile . highlight = false ;
275- const isUnder =
276- mouse . x >= tile . clientLeft &&
277- mouse . x <= tile . clientRight &&
278- mouse . y >= tile . clientTop &&
279- mouse . y <= tile . clientBottom ;
280- if ( ! isUnder ) return under ;
281- if ( under ) return tile . z > under . z ? tile : under ;
282- return tile ;
283- } ,
284- null as BoardTile | null ,
285- ) ;
286- if ( underMouse ) underMouse . highlight = true ;
287-
288277 const toDraw =
289278 introduction !== board . length ? board . slice ( 0 , ++ introduction ) : board ;
290279 const sorted = toDraw . toSorted ( ( a , b ) => {
0 commit comments