• + 0 comments

    My anser in Typescript, explain includes

    const matrixPrinter = (matrix: number[][]): void => {
        matrix.forEach(row => console.log(row.join(' ')));
    }
    function matrixRotation(matrix: number[][], r: number): void {
        /**
         * the size of the maxtrix at circle 1
         */
        const bound_y = matrix.length
        const bound_x = matrix[0].length
    
        /**
         * read matrix and bind it to [hmap] to easy calculate/reposition after
         */
        let hmap: { [cordinate: string]: number } = {}
        matrix.forEach((row, y) => row.forEach((cell, x) => {
            // counting [c], the circle indexed, start from 1
            let c = 1
            while (true) {
                if (y + 1 == c || y + 1 == bound_y - c + 1) break
                if (x + 1 == c || x + 1 == bound_x - c + 1) break
                c++
            }
    
            hmap[`${x + 1}:${y + 1}:${c}`] = cell;
        }))
    
        /**
         * calculate/reposition each number in [hmap]
         * 
         * + [hmap_temp] create to holding reposition number, avoid to override value
         * + [x],[y] is cordinate of number
         * + [v] is the number
         * + [c] is the circle index where the number is in (from outside to inside, start from 1)
         * + [rc] is the remaining rotations of the circle [c]
         * 
         * Q: why each number have [c] and [rc]?
         * A: cause each number can be on different circle, different circle will need different 
         *    number of [r] to complete fully rotation
         * 
         * Q: why [rc] look so complicated?
         * A: the hour hand at 10AM today and 10AM tomorow is the same. we dont need to calculate 
         *    the between. save resources and time.
         *      
         *      EG: with input [r]=20 and matrix [5x4], we have 2 circle to calculate
         *        
         *      circle 1 
         *      moves to complete fully circe: (5 - 1) * 2 + (4 - 1) * 2 = 14
         *      moves we need to real calculate: 20 % 14 = 6
         * 
         *      circle 2 (the size reduce from 5x4 to 3x2, -2 from each side)
         *      moves to complete fully circe: (3 - 1) * 2 + (2 - 1) * 2 = 6
         *      moves we need to real calculate: 20 % 6 = 2
         */
        let hmap_temp: typeof hmap = {}
        Object.keys(hmap).forEach(key => {
            let x = Number(key.split(':')[0]);
            let y = Number(key.split(':')[1]);
            let c = Number(key.split(':')[2]);
            let v = Number(hmap[key]);
    
            let c_size = { x: bound_x - (c - 1) * 2, y: bound_y - (c - 1) * 2 }
            let c_rotations = r % ((c_size.x - 1) * 2 + (c_size.y - 1) * 2)
    
            while (c_rotations > 0) {
                let direction: 'up' | 'down' | 'left' | 'right'
    
                // calculate move direction (for number on the edge) ...
                if (x == c) direction = 'down'
                if (x == bound_x - c + 1) direction = 'up'
                if (y == c) direction = 'left'
                if (y == bound_y - c + 1) direction = 'right'
    
                // calculate move direction (for number on the corner) ...
                if (x == c && y == c) direction = 'down'
                if (x == c && y == bound_y - c + 1) direction = 'right'
                if (x == bound_x - c + 1 && y == bound_y - c + 1) direction = 'up'
                if (x == bound_x - c + 1 && y == c) direction = 'left'
    
                // this is why using [hmap] is easyer, just change [x] and [y] to move the number
                switch (direction) {
                    case 'up': y--; break
                    case 'down': y++; break
                    case 'left': x--; break
                    case 'right': x++; break
                }
    
                c_rotations--
            }
    
            // store number and it new cordinate to new [hmap]
            hmap_temp[`${x}:${y}`] = v
        })
    
        // now we got new [hmap] stored rotated matrix info, print it
        matrixPrinter(
            new Array(bound_y).fill(0).map((row, y) =>
                new Array(bound_x).fill(0).map((_, x) =>
                    hmap_temp[`${x + 1}:${y + 1}`]
                )
            )
        );
    }