import {STextAlign} from "@persagy-web/draw/lib";
<template>
    <div>
        <canvas id="canvas" width="410" height="410" style="background: #bbada0;border-radius: 10px" tabindex="0"></canvas>
        <el-button @click="reset" type="primary">重新开始</el-button>
    </div>
</template>

<script lang="ts">
    import {Component, Vue} from "vue-property-decorator"
    import {SCanvasView, SColor, SPainter, STextAlign, STextBaseLine} from "@persagy-web/draw/lib";

    /**
     * 2048
     *
     * @author 郝洁 <haojie@persagy.com>
     */
    class GameView extends SCanvasView {
        /** 格子宽度 */
        box_width: number = this.width * 0.8 * 0.25;
        margin_width: number = this.width * 0.2 * 0.20;
        /** */
        gridMap: number[][] = [];
        /** */
        score: number = 0;
        /** 游戏结束 */
        isGameOver: boolean = false;
        /** 颜色 */
        color = {
            0:{
                font: "#ffffffff",
                background: "#eee4da59"
            },
            2: {
                font: "#776e65",
                background: "#eee4da"
            },
            4: {
                font: "#776e65",
                background: "#ede0c8"
            },
            8: {
                font: "#f9f6f2",
                background: "#f2b179"
            },
            16: {
                font: "#f9f6f2",
                background: "#f59563"
            },
            32: {
                font: "#f9f6f2",
                background: "#f67c5f"
            },
            64: {
                font: "#f9f6f2",
                background: "#f65e3b"
            },
            128: {
                font: "#f9f6f2",
                background: "#edcf72"
            },
            256: {
                font: "#f9f6f2",
                background: "#edcc61"
            },
            512: {
                font: "#f9f6f2",
                background: "#edc850"
            },
            1024: {
                font: "#f9f6f2",
                background: "#edc53f"
            },
            2048: {
                font: "#f9f6f2",
                background: "#edc22e"
            },
        };


        /**
         * 构造函数
         *
         * @param id canvas DOM id
         */
        constructor(id: string) {
            super(id);
            this.init()
        }

        /**
         * 初始化游戏
         */
        init(): void {
            this.gridMap = [];
            for (let row = 0; row < 4; row++) {    // 循环行数
                const m1: number[] = [];
                for (let col = 0; col < 4; col++) {    // 循环列数
                    m1.push(0);
                }
                this.gridMap.push(m1);
            }

            // 初始化分数
            this.score = 0;
            // 游戏结束标记为 false
            this.isGameOver = false;
            // 生成随机位置的数字
            this.generateOneNumber('left');
        }

        /**
         * 键盘按下事件
         *
         * @param event   事件对象
         */
        onKeyDown(event: KeyboardEvent): void {
            if (this.isGameOver) {
                return;
            }
            let moved, merged;
            // 按键判断
            switch (event.code) {
                case "KeyW": // 上移动作
                case "ArrowUp":
                    moved = this.moveToFill('top');
                    merged = this.mergeGrid('top');
                    (moved || merged) && this.generateOneNumber('top');
                    break;

                case "KeyA": // 左移动作
                case "ArrowLeft":
                    moved = this.moveToFill('left');
                    merged = this.mergeGrid('left');
                    (moved || merged) && this.generateOneNumber('left');
                    // this.update()
                    break;

                case "KeyD":    // 右移动作
                case "ArrowRight":
                    // 判断游戏是否继续，左部产生随机位置随机数
                    moved = this.moveToFill('right');
                    merged = this.mergeGrid('right');
                    (moved || merged) && this.generateOneNumber('right');
                    break;
                // 按键 下
                case "KeyS": // 下移动作
                case "ArrowDown":
                    // 判断游戏是否继续，上部产生随机位置随机数
                    moved = this.moveToFill('bottom');
                    merged = this.mergeGrid('bottom');
                    (moved || merged) && this.generateOneNumber('bottom');
                    break;
                default :
                // do nothing
            }
            this.isGameOver = this.checkIsOver();
        }

        /**
         * 生成随机的格子
         */
        generateOneNumber(direction: string): void {
            let temp = [];
            if (direction == "left") {
                for (let i = 0; i < 4; i++) {
                    temp.push(this.gridMap[i][3])
                }
            } else if (direction == "top") {
                temp = this.gridMap[3];
            } else if (direction == "bottom") {
                temp = this.gridMap[0];
            } else {
                // (direction == "right")
                for (let i = 0; i < 4; i++) {
                    temp.push(this.gridMap[i][0])
                }
            }

            let indexArr = [];
            for(let i = 0; i < temp.length; i++) {
                if(temp[i] == 0) {
                    indexArr.push(i)
                }
            }
            const len = indexArr.length;
            if (len) {
                const random = Math.floor(len * Math.random());
                const index = indexArr[random];
                if (direction == "left") {
                    this.gridMap[index][3] = 2
                } else if (direction == "top") {
                    this.gridMap[3][index] = 2;
                } else if (direction == "bottom") {
                    this.gridMap[0][index] = 2;
                } else {
                    // (direction == "right")
                    this.gridMap[index][0] = 2
                }
                this.update()
            } else {
                // this.isGameOver = true;
                this.update();
            }

            //随机一个位置，取新方块出现的位置

            //随机一个数字 2 或 4 ，在随机位置显示随机数字
        }

        /**
         * 移动数字,使移动方向的数字填满
         */
        moveToFill(direction: string): boolean{
            let flag = false;
            if (direction == "left") {
                for (let i = 0; i < 4; i++) {
                    const tempJrr = new Array(4).fill(0);
                    let index = 0;
                    for (let j = 0; j < 4; j++) {
                        if (this.gridMap[i][j]!=0) {
                            tempJrr[index] = this.gridMap[i][j];
                            index++;
                        } else {
                            flag = true;
                        }
                    }
                    this.gridMap[i] = tempJrr;
                }
            } else if (direction == "top") {
                for (let j = 0; j < 4; j++) {
                    const tempJrr = new Array(4).fill(0);
                    let index = 0;
                    for (let i = 0; i < 4; i++) {
                        if (this.gridMap[i][j]!=0) {
                            tempJrr[index] = this.gridMap[i][j];
                            index++;
                        } else {
                            flag = true;
                        }
                    }
                    for(let u = 0; u < 4; u++) {
                        this.gridMap[u][j] = tempJrr[u];
                    }
                }
            } else if (direction == "bottom") {
                for (let j = 0; j < 4; j++) {
                    const tempJrr = new Array(4).fill(0);
                    let index = 3;
                    for (let i = 3; i >= 0; i--) {
                        if (this.gridMap[i][j]!=0) {
                            tempJrr[index] = this.gridMap[i][j];
                            index--;
                        } else {
                            flag = true;
                        }
                    }
                    for(let u = 3; u >= 0; u--) {
                        this.gridMap[u][j] = tempJrr[u];
                    }
                }
            } else {
                for (let i = 0; i < 4; i++) {
                    const tempJrr = new Array(4).fill(0);
                    let index = 3;
                    for (let j = 3; j >= 0; j--) {
                        if (this.gridMap[i][j]!=0) {
                            tempJrr[index] = this.gridMap[i][j];
                            index--;
                        } else {
                            flag = true;
                        }
                    }
                    this.gridMap[i] = tempJrr;
                }
            }
            return flag;
        }

        /**
         * 合并数字
         *
         * @param direction     方向
         */
        mergeGrid(direction: string): boolean{
            let flag = false;
            if (direction == "left") {
                for (let i = 0; i < 4; i++) {
                    for (let j = 0; j < 3; j++) {
                        if (this.gridMap[i][j] == this.gridMap[i][j + 1]) {
                            this.gridMap[i][j] = 2*this.gridMap[i][j];
                            this.gridMap[i][j + 1] = 0;
                            flag = true;
                        }
                    }
                }
            } else if (direction == "top") {
                for (let i = 0; i < 3; i++) {
                    for (let j = 0; j < 4; j++) {
                        if (this.gridMap[i][j] == this.gridMap[i + 1][j]) {
                            this.gridMap[i][j] = 2*this.gridMap[i][j];
                            this.gridMap[i + 1][j] = 0;
                            flag = true;
                        }
                    }
                }
            } else if (direction == "bottom") {
                for (let i = 3; i >= 1; i--) {
                    for (let j = 0; j < 4; j++) {
                        if (this.gridMap[i][j] == this.gridMap[i - 1][j]) {
                            this.gridMap[i][j] = 2*this.gridMap[i][j];
                            this.gridMap[i - 1][j] = 0;
                            flag = true;
                        }
                    }
                }
            } else {
                // (direction == "right")
                for (let i = 0; i < 4; i++) {
                    for (let j = 3; j >= 1; j--) {
                        if (this.gridMap[i][j] == this.gridMap[i][j - 1]) {
                            this.gridMap[i][j] = 2*this.gridMap[i][j];
                            this.gridMap[i][j - 1] = 0;
                            flag = true;
                        }
                    }
                }
            }
            if (flag) {
                this.moveToFill(direction)
            }
            return flag;
        }

        /**
         * 绘制游戏画面
         *
         * @param painter   绘制对象
         */
        onDraw(painter: SPainter): void {
            painter.clearRect(0, 0, this.width, this.height);
            painter.pen.color = SColor.Transparent;
            this.drawMap(painter);
            this.drawNum(painter);

            // 如果游戏结束，绘制文字
            if (this.isGameOver) {
                painter.brush.color = SColor.Blue;
                painter.font.size = 20;
                painter.font.textAlign = STextAlign.Center;
                painter.drawText("GAME OVER", 180, 30);
                // return;
            }
        }

        /**
         * 绘制数字
         */
        drawNum(painter: SPainter): void {
            // 遍历行
            for (let row = 0; row < 4; row++) {
                // 遍历列
                for (let col = 0; col < 4; col++) {
                    this.drawCellNum(painter, row, col, this.gridMap[row][col]);
                }
            }
        }

        /**
         * 绘制格子中的数字
         */
        drawCellNum(painter: SPainter, row: number, col: number, type: number): void {
            const x = col * 100 + 10;
            const y = row * 100 + 10;
            if (type != 0) {
                painter.font.textAlign = STextAlign.Center;
                painter.font.size = 40;
                // @ts-ignore
                painter.font.textBaseLine = STextBaseLine.Middle;
                painter.brush.color = new SColor(this.color[type].font);
                painter.drawText(type+'', x+45, y+45);
            }
        }

        /**
         * 绘制背景图
         */
        drawMap(painter: SPainter) {
            // 遍历行
            for (let row = 0; row < 4; row++) {
                // 遍历列
                for (let col = 0; col < 4; col++) {
                    this.drawCell(painter, row, col, this.gridMap[row][col]);
                }
            }
        }

        /**
         * 绘制具体格子
         *
         * @param painter   绘制对象
         * @param row       行
         * @param col       列
         * @param type      图类型
         */
        private drawCell(painter: SPainter, row: number, col: number, type: number): void {
            const x = col * 100 + 10;
            const y = row * 100 + 10;
            painter.brush.color = new SColor(this.color[type].background);
            painter.drawRoundRect(x, y, 90, 90, 10);
        }


        /**
         * 检测结束
         */
        checkIsOver():boolean{
            for(let i = 0; i < 4; i++) {
                if (this.gridMap[i].indexOf(0) > -1) {
                    return false;
                }
                for(let j = 0; j < 3; j++) {
                    if (this.gridMap[i][j] == this.gridMap[i][j+1]) {
                        return false
                    }
                    if (i < 3 && this.gridMap[i][j] == this.gridMap[i+1][j]){
                        return false
                    }
                }
            }
            return this.gridMap[3][3] != this.gridMap[2][3];
        }
    }

    @Component
    export default class Gcanvas extends Vue {
        /** 实例化 */
        view: GameView | null = null;

        /**
         * 页面挂载完成
         */
        mounted(): void {
            this.view = new GameView('canvas');
            document.getElementById('canvas')!!.focus()
        }

        /**
         * 重置操作
         */
        reset(): void {
            this.view!!.init()
            document.getElementById("canvas")!!.focus()
        }
    }
</script>

<style scoped>

</style>
