2

I'm making a 4x4 number puzzle where the user is only able to drag adjacent tiles from the empty tile and drop it in the empty type to swap position. I'm using the method isDraggable to figure out if the tile is adjacent to the empty tile and feeding the boolean value into the Tile object which then makes the tile draggable or not. Right now I'm not able to drag the adjacent tiles into the empty tile.

Board class

import React from 'react';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
import {Tile} from './Tile'
import './Number_puzzle.css';

export class Number_puzzle extends React.Component {


     constructor(props){
         super(props);
         this.state = {
             numbers : [ [1,2,3,4],
                 [5,6,7,8],
                 [9,10,11,12],
                 [13,14,15,-1] ] // -1 is empty
         };
         this.shuffle2darray(this.state.numbers)
         console.log("shuffled!")
    }

    shuffle2darray(numbers){
        let randomI = -1
        let randomJ = -1

        for ( let i = 0; i < numbers.length; i++){
            for ( let j = 0; j < numbers[i].length; j++){
                while(randomI !== i && randomJ !== j){ // prevent swapping with itself
                    randomI = Math.floor(Math.random()*4)
                    randomJ = Math.floor(Math.random()*4)
                }

                const holder = numbers[randomI][randomJ]
                numbers[randomI][randomJ] = numbers[i][j]
                numbers[i][j] = holder

                randomI = -1
                randomJ = -1
            }
        }
    }

    isDraggable(tileRow, tileCol){
        for (let i = 0; i < this.state.numbers.length; i++) {
            for (let j = 0; j < this.state.numbers[i].length; j++) {
                if (this.state.numbers[i][j] === -1) {
                    if( tileRow === i && tileCol === (j-1))
                        return true;
                    if( tileRow === (i-1) && tileCol === j)
                        return true;
                    if( tileRow === (i+1) && tileCol === j)
                        return true;
                    if( tileRow === i && tileCol === (j+1))
                        return true;
                    else
                        return false
                }
            }
        }
    }


    handleDrag(fromIndex, toIndex) {
        if (this.isDraggable(fromIndex[0], toIndex[0])) {
            const numbers = this.state.numbers.slice(); // make a copy of the 2D array by slice and not modifying numbers directly
            numbers[toIndex[0]][toIndex[1]] = numbers[fromIndex[0]][fromIndex[1]];
            numbers[fromIndex[0]][fromIndex[1]] = -1;
            this.setState({ numbers: numbers }); // update the state with the new 2D array
        }
    }

    render() {
        return (
            <DndProvider backend={HTML5Backend}>
                <div className="grid-container">
                    {this.state.numbers.map((row, i) => (
                        <React.Fragment key={i}>
                            {row.map((number, j) => (
                                <div key={`${i}${j}`}>
                                    {console.log("number: " + number)}
                                    {console.log("draggable: " + this.isDraggable(i, j))}
                                    <Tile
                                        number={number}
                                        position={[i, j]}
                                        isAdjacent={this.isDraggable(i, j)}
                                        onDrop={this.handleDrag.bind(this)}
                                    />
                                </div>
                            ))}
                        </React.Fragment>
                    ))}
                </div>
            </DndProvider>
        );
    }

}

Tile class

  import React from 'react';
    import { useDrag, useDrop } from 'react-dnd';
    
    export function Tile({ number, position, isAdjacent, onDrop }) {
        const [{ isDragging }, drag] = useDrag({
            type: 'tile',
            item: { position },
            canDrag: isAdjacent,
            collect: (monitor) => ({
                isDragging: !!monitor.isDragging(),
            }),
        });
    
        const [{ isOver }, drop] = useDrop({
            accept: 'tile',
            canDrop: () => {
                console.log("number", number)
                if (number === -1) {
                    return true;
                }
                return isAdjacent;
            },
            drop: (item) => onDrop(item.position, position),
            collect: (monitor) => ({
                isOver: !!monitor.isOver(),
            }),
        });
    
        const style = {
            opacity: isDragging ? 0.5 : 1,
            backgroundColor: isAdjacent ? 'lightgreen' : 'white',
            border: isOver ? '2px dashed black' : 'none'
        };
    
        return (
            <div
                draggable={isAdjacent}
                onDragStart={(e) => {
                    e.dataTransfer.setData('text/plain', ''); // required for Firefox
                    drag();
                }}
                ref={(node) => {
                    if (node !== null) {
                        drag(node.firstChild);
                    }
                    drop(node);
                }}
                style={style}
            >
                {number !== -1 && (
                    <div ref={drag} style={{ cursor: 'move' }}>
                        {number}
                    </div>
                )}
            </div>
        );
    }
bryan lee
  • 33
  • 4

0 Answers0