• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16@Entry
17@Component
18struct GameCard {
19  @State btnFontSize: string = '20';
20  @State infoFontSize: string = '30';
21  private BLACK_WIN: string = 'BLACK WINS!';
22  private WHITE_WIN: string = 'WHITE WINS!';
23  private BLACK_FIRST: string = 'BLACK FIRST';
24  private BLACK_STEP: string = 'BLACK STEP';
25  private WHITE_STEP: string = 'WHITE STEP';
26  private ALREADY_STEP: string = 'ALREADY STEP';
27  private setInfo: RenderingContextSettings = new RenderingContextSettings(true);
28  private ctxInfo: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.setInfo);
29  private settings: RenderingContextSettings = new RenderingContextSettings(true);
30  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
31  private infoAreaWidth: number = 0;
32  private infoAreaHeight: number = 0;
33  private canvasWidth: number = 0;
34  private canvasHeight: number = 0;
35  private chessboardMargin: number = 0.05;
36  private chessboardPosX: number = 0;
37  private chessboardPosY: number = 0;
38  private chessboardWidth: number = 0;
39  private chessboardHeight: number = 0;
40  private chessboardLines: number = 14;
41  private chessboardDotInfo: number = 4;
42  private chessboardLineWidth: number = 0;
43  private chessboardDotRadius: number = 0;
44  private chessPiecesCount: number = 0;
45  private chessPiecesArray: Array<object> = [];
46  private processRecord: Array<object> = [];
47
48  chessPiecesInit(areaWidth: number, areaHeight: number): void {
49    this.canvasWidth = areaWidth;
50    this.canvasHeight = areaHeight;
51    this.chessboardPosX = this.canvasWidth * this.chessboardMargin;
52    this.chessboardPosY = this.canvasHeight * this.chessboardMargin;
53    this.chessboardWidth = this.canvasWidth - this.chessboardPosX * 2;
54    this.chessboardHeight = this.canvasHeight - this.chessboardPosY * 2;
55    this.processRecord = new Array();
56    this.chessPiecesArray = new Array();
57    for (let i = 0; i <= this.chessboardLines; ++i) {
58      this.chessPiecesArray[i] = new Array();
59      for (let j = 0; j <= this.chessboardLines; ++j) {
60        // -1 for blank space; 0 for black chess; 1 for white chess
61        this.chessPiecesArray[i][j] = -1;
62      }
63    }
64    console.info(`[ArkTSCard] ${this.chessboardWidth} -- ${this.chessboardHeight}`)
65  }
66
67  restart(): void {
68    console.info('[ArkTSCard] Paint chessboard restart');
69    this.chessPiecesCount = 0;
70    this.processRecord.length = 0;
71    for (let i = 0; i <= this.chessboardLines; ++i) {
72      for (let j = 0; j <= this.chessboardLines; ++j) {
73        // -1 for blank space; 0 for black chess; 1 for white chess
74        this.chessPiecesArray[i][j] = -1;
75      }
76    }
77    this.paintStartInfo();
78    this.paintBackground();
79    this.paintChessboard();
80  }
81
82  paintForSizeChanged(areaWidth: number, areaHeight: number): void {
83    console.info(`[ArkTSCard] ${this.canvasWidth}-${this.canvasHeight} to ${areaWidth}-${areaHeight}`);
84    this.canvasWidth = areaWidth;
85    this.canvasHeight = areaHeight;
86    this.chessboardPosX = this.canvasWidth * this.chessboardMargin;
87    this.chessboardPosY = this.canvasHeight * this.chessboardMargin;
88    this.chessboardWidth = this.canvasWidth - this.chessboardPosX * 2;
89    this.chessboardHeight = this.canvasHeight - this.chessboardPosY * 2;
90    this.paintBackground();
91    this.paintChessboard();
92    for (let i = 0; i < this.processRecord.length; ++i) {
93      let process = this.processRecord[i];
94      this.paintChessPieces(process[0], process[1], i);
95    }
96    this.paintProcess();
97  }
98
99  fallback(): void {
100    console.info(`[ArkTSCard] Fallback ${this.processRecord.length} - ${this.chessPiecesCount}`);
101    if (this.chessPiecesCount <= 0) {
102      this.paintStartInfo();
103      return;
104    }
105    let prev: object | undefined = this.processRecord.pop();
106
107    if (prev) {
108      this.chessPiecesArray[prev[0]][prev[1]] = -1;
109      this.paintBackground();
110      this.paintChessboard();
111      for (let i = 0; i < this.processRecord.length; ++i) {
112        let process = this.processRecord[i];
113        this.paintChessPieces(process[0], process[1], i);
114      }
115      this.chessPiecesCount--;
116      this.paintProcess();
117    }
118
119  }
120
121  paintBackground(): void {
122    console.info('[ArkTSCard] Paint Background');
123    this.context.fillStyle = 'rgba(250, 204, 164, 1.00)';
124    this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
125    this.context.fillStyle = 'rgba(255, 119, 0, 1.00)';
126    this.context.fillRect(this.chessboardPosX, this.chessboardPosY, this.chessboardWidth, this.chessboardHeight);
127  }
128
129  paintChessboard(): void {
130    console.info('[ArkTSCard] Paint Chessboard');
131    let widthGap = this.chessboardWidth / this.chessboardLines;
132    let heightGap = this.chessboardHeight / this.chessboardLines;
133    this.context.beginPath();
134    this.context.strokeStyle = '#000000';
135    this.context.lineWidth = this.chessboardLineWidth;
136    for (let i = 0; i <= this.chessboardLines; ++i) {
137      let startPosX = this.chessboardPosX;
138      let startPosY = this.chessboardPosY + i * heightGap;
139      let endPosX = this.chessboardPosX + this.chessboardWidth;
140      let endPosY = this.chessboardPosY + i * heightGap;
141      this.context.moveTo(startPosX, startPosY);
142      this.context.lineTo(endPosX, endPosY);
143      console.info(`[ArkTSCard] Paint ${startPosX} ${startPosY} ${endPosX} ${endPosY}`);
144    }
145    for (let i = 0; i <= this.chessboardLines; ++i) {
146      let startPosX = this.chessboardPosX + i * widthGap;
147      let startPosY = this.chessboardPosY;
148      let endPosX = this.chessboardPosX + i * widthGap;
149      let endPosY = this.chessboardPosY + this.chessboardHeight;
150      this.context.moveTo(startPosX, startPosY);
151      this.context.lineTo(endPosX, endPosY);
152      console.info(`[ArkTSCard] Paint ${startPosX} ${startPosY} ${endPosX} ${endPosY}`);
153    }
154    this.context.stroke();
155    this.context.closePath();
156
157    let dotIndex = this.chessboardLines % this.chessboardDotInfo;
158    this.paintChessboardDot(
159      this.chessboardPosX + dotIndex * widthGap,
160      this.chessboardPosY + dotIndex * heightGap,
161      this.chessboardDotRadius, '#000000'
162    );
163    this.paintChessboardDot(
164      this.chessboardPosX + (this.chessboardLines - dotIndex) * widthGap,
165      this.chessboardPosY + dotIndex * heightGap,
166      this.chessboardDotRadius, '#000000'
167    );
168    this.paintChessboardDot(
169      this.chessboardPosX + dotIndex * widthGap,
170      this.chessboardPosY + (this.chessboardLines - dotIndex) * heightGap,
171      this.chessboardDotRadius, '#000000'
172    );
173    this.paintChessboardDot(
174      this.chessboardPosX + (this.chessboardLines - dotIndex) * widthGap,
175      this.chessboardPosY + (this.chessboardLines - dotIndex) * heightGap,
176      this.chessboardDotRadius, '#000000'
177    );
178  }
179
180  paintChessboardDot(posX: number, poxY: number, radius: number, color: string): void {
181    this.context.beginPath();
182    this.context.moveTo(posX - radius, poxY);
183    this.context.arc(posX, poxY, radius, 2 * Math.PI, 0, true);
184    this.context.strokeStyle = color;
185    this.context.closePath();
186    this.context.fillStyle = color;
187    this.context.fill();
188  }
189
190  processClick(posX: number, posY: number): void {
191    let widthGap = this.chessboardWidth / this.chessboardLines;
192    let heightGap = this.chessboardHeight / this.chessboardLines;
193    let chessIndexX = Math.round((posX - this.chessboardPosX) / widthGap);
194    let chessIndexY = Math.round((posY - this.chessboardPosY) / heightGap);
195    chessIndexX = Math.min(chessIndexX, this.chessboardLines);
196    chessIndexY = Math.min(chessIndexY, this.chessboardLines);
197    if (chessIndexX < 0 || chessIndexY < 0) {
198      return;
199    }
200    if (this.chessPiecesArray[chessIndexX][chessIndexY] != -1) {
201      this.paintPrompting();
202      return;
203    }
204    console.info(`[ArkTSCard] Click:[${posX} ${posY}] Put:[${chessIndexX} ${chessIndexY}]`);
205    this.chessPiecesArray[chessIndexX][chessIndexY] = this.chessPiecesCount % 2;
206    let currentProcess = [chessIndexX, chessIndexY];
207    this.processRecord.push(currentProcess);
208    this.paintChessPieces(chessIndexX, chessIndexY, this.chessPiecesCount);
209    this.chessPiecesCount++;
210    this.paintProcess();
211    this.checkVictoryDefeat();
212  }
213
214  paintChessPieces(chessIndexX: number, chessIndexY: number, count: number): void {
215    let widthGap = this.chessboardWidth / this.chessboardLines;
216    let heightGap = this.chessboardHeight / this.chessboardLines;
217    let drawPosX = chessIndexX * widthGap + this.chessboardPosX;
218    let drawPosY = chessIndexY * heightGap + this.chessboardPosY;
219    let chessPiecesRadius = Math.min(widthGap, heightGap) / 2;
220    let color: string = count % 2 == 0 ? '#000000' : '#ffffff';
221    console.info(`[ArkTSCard] Put:[${chessIndexX} ${chessIndexY}] draw:[${drawPosX} ${drawPosY}]`);
222    this.paintChessboardDot(drawPosX, drawPosY, chessPiecesRadius, color);
223  }
224
225  dfsCheckCountLW(type: number, count: number, indexX: number, indexY: number): number {
226    let curCount: number = count;
227    if (indexX - 1 >= 0) { // Left
228      if (this.chessPiecesArray[indexX - 1][indexY] == type) {
229        this.chessPiecesArray[indexX - 1][indexY] = type + 100; // Avoid dead cycle
230        curCount = Math.max(curCount, this.dfsCheckCountLW(type, count + 1, indexX - 1, indexY));
231      }
232    }
233    if (indexX + 1 <= this.chessboardLines) { // Write
234      if (this.chessPiecesArray[indexX + 1][indexY] == type) {
235        this.chessPiecesArray[indexX + 1][indexY] = type + 100; // Avoid dead cycle
236        curCount = Math.max(curCount, this.dfsCheckCountLW(type, count + 1, indexX + 1, indexY));
237      }
238    }
239    return curCount;
240  }
241
242  dfsCheckCountUD(type: number, count: number, indexX: number, indexY: number): number {
243    let curCount: number = count;
244    if (indexY - 1 >= 0) { // Up
245      if (this.chessPiecesArray[indexX][indexY - 1] == type) {
246        this.chessPiecesArray[indexX][indexY - 1] = type + 100; // Avoid dead cycle
247        curCount = Math.max(curCount, this.dfsCheckCountUD(type, count + 1, indexX, indexY - 1));
248      }
249    }
250    if (indexY + 1 <= this.chessboardLines) { // Down
251      if (this.chessPiecesArray[indexX][indexY + 1] == type) {
252        this.chessPiecesArray[indexX][indexY + 1] = type + 100; // Avoid dead cycle
253        curCount = Math.max(curCount, this.dfsCheckCountUD(type, count + 1, indexX, indexY + 1));
254      }
255    }
256    return curCount
257  }
258
259  dfsCheckCountLU(type: number, count: number, indexX: number, indexY: number): number {
260    let curCount: number = count;
261    if (indexX - 1 >= 0 && indexY - 1 >= 0) { // Left & Up
262      if (this.chessPiecesArray[indexX - 1][indexY -1] == type) {
263        this.chessPiecesArray[indexX - 1][indexY -1] = type + 100; // Avoid dead cycle
264        curCount = Math.max(curCount, this.dfsCheckCountLU(type, count + 1, indexX - 1, indexY - 1));
265      }
266    }
267    if (indexX + 1 <= this.chessboardLines && indexY + 1 <= this.chessboardLines) { // Write & Down
268      if (this.chessPiecesArray[indexX + 1][indexY + 1] == type) {
269        this.chessPiecesArray[indexX + 1][indexY + 1] = type + 100; // Avoid dead cycle
270        curCount = Math.max(curCount, this.dfsCheckCountLU(type, count + 1, indexX + 1, indexY + 1));
271      }
272    }
273    return curCount;
274  }
275
276  dfsCheckCountWU(type: number, count: number, indexX: number, indexY: number): number {
277    let curCount: number = count;
278    if (indexX + 1 <= this.chessboardLines && indexY - 1 >= 0) { // Write & Up
279      if (this.chessPiecesArray[indexX + 1][indexY - 1] == type) {
280        this.chessPiecesArray[indexX + 1][indexY - 1] = type + 100; // Avoid dead cycle
281        curCount = Math.max(curCount, this.dfsCheckCountWU(type, count + 1, indexX + 1, indexY - 1));
282      }
283    }
284    if (indexX - 1 >= 0 && indexY + 1 <= this.chessboardLines) { // Left & Down
285      if (this.chessPiecesArray[indexX - 1][indexY + 1] == type) {
286        this.chessPiecesArray[indexX - 1][indexY + 1] = type + 100; // Avoid dead cycle
287        curCount = Math.max(curCount, this.dfsCheckCountWU(type, count + 1, indexX - 1, indexY + 1));
288      }
289    }
290    return curCount;
291  }
292
293  restoreChessPiecesArray(): void {
294    for (let i = 0; i <= this.chessboardLines; ++i) {
295      for (let j = 0; j <= this.chessboardLines; ++j) {
296        if (this.chessPiecesArray[i][j] >= 10) {
297          this.chessPiecesArray[i][j] -= 100;
298        }
299      }
300    }
301  }
302
303  dfsCheckCount(type: number, count: number, indexX: number, indexY: number): number {
304    let curCount = count;
305    this.chessPiecesArray[indexX][indexY] += 100; // Avoid dead cycle
306    curCount = Math.max(curCount, this.dfsCheckCountLW(type, count, indexX, indexY));
307    this.restoreChessPiecesArray();
308    this.chessPiecesArray[indexX][indexY] += 100; // Avoid dead cycle
309    curCount = Math.max(curCount, this.dfsCheckCountUD(type, count, indexX, indexY));
310    this.restoreChessPiecesArray();
311    this.chessPiecesArray[indexX][indexY] += 100; // Avoid dead cycle
312    curCount = Math.max(curCount, this.dfsCheckCountLU(type, count, indexX, indexY));
313    this.restoreChessPiecesArray();
314    this.chessPiecesArray[indexX][indexY] += 100; // Avoid dead cycle
315    curCount = Math.max(curCount, this.dfsCheckCountWU(type, count, indexX, indexY));
316    this.restoreChessPiecesArray();
317    return curCount;
318  }
319
320  checkVictoryDefeat(): void {
321    let countWhite = 0;
322    let countBlack = 0;
323    for (let i = 0; i <= this.chessboardLines; ++i) {
324      for (let j = 0; j <= this.chessboardLines; ++j) {
325        if (this.chessPiecesArray[i][j] == -1) {
326          continue;
327        }
328        if (this.chessPiecesArray[i][j] == 0) {
329          countBlack = Math.max(countBlack, this.dfsCheckCount(0, 1, i, j));
330          if (countBlack >= 5) {
331            break;
332          }
333        }
334        if (this.chessPiecesArray[i][j] == 1) {
335          countWhite = Math.max(countWhite, this.dfsCheckCount(1, 1, i, j));
336          if (countWhite >= 5) {
337            break;
338          }
339        }
340      }
341    }
342    for (let i = 0; i <= this.chessboardLines; ++i) {
343      for (let j = 0; j <= this.chessboardLines; ++j) {
344        if (this.chessPiecesArray[i][j] >= 10) {
345          this.chessPiecesArray[i][j] -= 100;
346        }
347      }
348    }
349    console.info(`[ArkTSCard] countBlack:[${countBlack}] countWhite:[${countWhite}]`);
350    if (countBlack >= 5) {
351      this.paintGameOver(0);
352    }
353    if (countWhite >= 5) {
354      this.paintGameOver(1);
355    }
356  }
357
358  paintPrompting(): void {
359    console.info('[ArkTSCard] Paint prompting info');
360    this.ctxInfo.fillStyle = 'rgba(207, 186, 170, 1.00)';
361    this.ctxInfo.fillRect(0, 0, this.infoAreaWidth, this.infoAreaHeight);
362    this.context.textAlign = 'center';
363    this.ctxInfo.fillStyle = 'rgba(0, 0, 0, 1.00)';
364    this.ctxInfo.font = this.infoFontSize + ' sans-serif';
365    this.ctxInfo.fillText(this.ALREADY_STEP, 0, this.infoAreaHeight / 1.5);
366  }
367
368  paintProcess(): void {
369    console.info('[ArkTSCard] Paint process info');
370    let info = this.BLACK_STEP;
371    if (this.chessPiecesCount % 2 == 1) {
372      info = this.WHITE_STEP;
373    }
374    this.ctxInfo.fillStyle = 'rgba(207, 186, 170, 1.00)';
375    this.ctxInfo.fillRect(0, 0, this.infoAreaWidth, this.infoAreaHeight);
376    this.context.textAlign = 'center';
377    this.ctxInfo.fillStyle = 'rgba(0, 0, 0, 1.00)';
378    this.ctxInfo.font = this.infoFontSize + ' sans-serif';
379    this.ctxInfo.fillText(info, 0, this.infoAreaHeight / 1.5);
380  }
381
382  paintStartInfo(): void {
383    console.info('Paint Start info');
384    this.ctxInfo.fillStyle = 'rgba(207, 186, 170, 1.00)';
385    this.ctxInfo.fillRect(0, 0, this.infoAreaWidth, this.infoAreaHeight);
386    this.context.textAlign = 'center';
387    this.ctxInfo.fillStyle = 'rgba(0, 0, 0, 1.00)';
388    this.ctxInfo.font = this.infoFontSize + ' sans-serif';
389    this.ctxInfo.fillText(this.BLACK_FIRST, 0, this.infoAreaHeight / 1.5);
390  }
391
392  paintGameOver(type: number): void {
393    console.info('[ArkTSCard] Paint game over');
394    let info = this.BLACK_WIN;
395    if (type == 1) {
396      info = this.WHITE_WIN;
397    }
398    this.ctxInfo.fillStyle = 'rgba(207, 186, 170, 1.00)';
399    this.ctxInfo.fillRect(0, 0, this.infoAreaWidth, this.infoAreaHeight);
400    this.context.textAlign = 'center';
401    this.ctxInfo.fillStyle = 'rgba(0, 0, 0, 1.00)';
402    this.ctxInfo.font = this.infoFontSize + ' sans-serif';
403    this.ctxInfo.fillText(info, 0, this.infoAreaHeight / 1.5);
404  }
405
406  build() {
407    Column() {
408      Column() {
409        Row() {
410          Column() {
411            Button($r('app.string.res_restart')).width('80%').height('95%')
412              .fontSize(this.btnFontSize)
413              .onClick(() => {
414                this.restart();
415              })
416          }.width('30%')
417
418          Column() {
419            Canvas(this.ctxInfo)
420              .width('100%')
421              .height('100%')
422              .backgroundColor('#fff5cccc')
423              .onReady(() => {
424                console.info('[ArkTSCard] onReady for canvas draw info');
425                this.infoAreaWidth = Number(this.ctxInfo.width);
426                this.infoAreaHeight = Number(this.ctxInfo.height);
427              })
428          }.width('40%').height('100%')
429          .backgroundColor('#fff8c6c6')
430
431          Column() {
432            Button($r('app.string.res_fallback')).width('80%').height('95%')
433              .fontSize(this.btnFontSize)
434              .onClick(() => {
435                this.fallback();
436              })
437          }.width('30%')
438        }
439      }.height('10%').width('100%')
440
441      Row() {
442        Canvas(this.context)
443          .width('100%')
444          .height('90%')
445          .onReady(() => {
446            let minSize = Math.min(this.context.width, this.context.height);
447            this.btnFontSize = minSize * 0.035 + 'vp';
448            this.infoFontSize = minSize * 0.09 + 'px';
449            this.chessboardLineWidth = minSize * 0.004;
450            this.chessboardDotRadius = this.chessboardLineWidth * 3;
451            if (this.canvasWidth === 0 && this.canvasHeight === 0) {
452              console.info('[ArkTSCard] onReady for canvas draw content');
453              this.chessPiecesInit(Number(this.context.width), Number(this.context.height));
454              this.paintBackground();
455              this.paintChessboard();
456              this.paintStartInfo();
457            } else {
458              console.info('[ArkTSCard] onReady for canvas size changed');
459              this.paintForSizeChanged(Number(this.context.width), Number(this.context.height));
460            }
461          })
462          .onClick((event: ClickEvent) => {
463            console.info(`[ArkTSCard] Click: PosX:${event.x} PosX:${event.y}`);
464            this.processClick(event.x, event.y);
465          })
466      }
467    }.height('100%').width('100%')
468  }
469
470  aboutToAppear() {
471    console.info('[ArkTSCard] aboutToAppear');
472  }
473
474  aboutToDisappear() {
475    console.info('[ArkTSCard] aboutToDisappear');
476  }
477}