1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Slide Puzzle をつくってみました

Last updated at Posted at 2022-02-19

Reactをちょっと使ってみたくて、スライドパズルをつくってみました。

Puzzle.png

少しこだわったところ

3個の駒をまとめて動かせる
Flutterのサンプルのパズル(旧版)のように、自動で動く

残念なところ

reactの使い方、javascriptの使い方、そしてデザインが残念です。

codesandbox

コード

import React from 'react';
import ReactDOM from 'react-dom';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Slider from '@mui/material/Slider';
import Typography from '@mui/material/Typography';
import { createTheme, ThemeProvider } from '@mui/material/styles';

const Tile = ({position, num, onClick}) => {
  const tileSize = 8 * 4 + 20 + 1 + 4;
  if (num === 0 ) { return null; }
  return (
    <Box
      sx={{
        userSelect:'none',
        border: '1px solid',
        p: 2,
        position: 'absolute',
        width: 20,
        height: 20,
        textAlign: 'center',
        top: Math.trunc(position/4) * tileSize + 10,
        left: position % 4 * tileSize + 10,
        transition:  (theme) => theme.transitions.create(['top', 'left'], 
        {
          duration:  300,
          easing: 'cubic-bezier(.1,-0.35,.21,1.62)'
        }),
      }}
      onClick = {() => onClick(position)}
    >
      {num}
    </Box>)
};

export default function Game() {
  const [tilePositions, setTilePositions] = React.useState( [...Array(16)].map((_, i) => i));
  const [autoMode, setAutoMode] = React.useState(false);
  const [autoUpDown, setAutoUpDown] = React.useState(false);
  const [updateInterval, setUpdateInterval] = React.useState(250);

  const sliderChange = (event, newValue) => {
    setUpdateInterval(newValue);
  };

  React.useEffect(() => {
    const moveAuto = () =>{
      let x = tilePositions[0] % 4;
      let y = Math.trunc(tilePositions[0] / 4);
      let [xx, yy] = [[0,1,2,3], [0,1,2,3]];
      xx.splice(x, 1);
      yy.splice(y, 1);
      const [moveToX, moveToY] = [xx[Math.floor(Math.random()*3)], yy[Math.floor(Math.random()*3)]] 
      move((autoUpDown ? x + 4 * moveToY : moveToX + 4 * y), tilePositions);
      setAutoUpDown(v => !v)
    }
    const timer = setTimeout(() => {
      if (autoMode){
        moveAuto();
      }
    }, updateInterval);
    return () => {
      clearTimeout(timer);
    };
  }, [autoMode, autoUpDown, tilePositions,updateInterval]);

  const shuffleTile = ([...positions]) =>{
    for (let i = 0; i < positions.length; i++) {
      const j = Math.floor(Math.random() * (positions.length -i)) + i;
      [positions[i], positions[j]] = [positions[j], positions[i]];
    }
    setTilePositions(positions);
  }

  const move = (posClicked, [...positions]) =>
  {
    const posEmpty = positions[0];
    const isMoveLeftRight = Math.trunc(posEmpty/4) === Math.trunc(posClicked/4);
    if (!isMoveLeftRight && !(posEmpty % 4 ===  posClicked % 4)) // && isMoveUpDown
    {
      return;
    }
    const deltaIndex = isMoveLeftRight ? 1: 4;
    const deltaPosition = Math.sign(posEmpty - posClicked) * deltaIndex;
    const pos = [...positions];
    const [minPos, maxPos]= [Math.min(posEmpty, posClicked), Math.max(posEmpty, posClicked)];
    for (let p = minPos; p < maxPos + 1; p += deltaIndex ){
      let index = positions.findIndex(v => v === p);
      p === posEmpty ? pos[0] = posClicked : pos[index] = p + deltaPosition;
    }
    setTilePositions(pos)
  }

  return (
    <ThemeProvider theme={createTheme({})}>
      <Box sx={{position: 'relative', ml: 'autoMode', mr: 'autoMode', width:220, height:300}}>
        <Typography>Slide Puzzle</Typography>
        <Button onClick={() => shuffleTile(tilePositions)}>Shuffle</Button>
        <Button onClick={() => setAutoMode(!autoMode)}>{autoMode ? 'manual' : 'automatic'}</Button>
        <Typography id="update-slider" gutterBottom>Automatic Update Interval</Typography>
        <Slider value={updateInterval} onChange={sliderChange} min={0} step={10} max={500} aria-labelledby="update-slider"/>
        <Box sx={{position: 'relative', ml: 'autoMode', mr: 'autoMode', width:250, height:250}}>
          {tilePositions.map((p, n) => { 
            return (<Tile key = {n} num = {n} position = {p} onClick = {(p) => move(p, tilePositions)}></Tile>)
          })}
        </Box>
      </Box>
    </ThemeProvider> 
  );
}

ReactDOM.render(
  <React.StrictMode>
    <Game />
  </React.StrictMode>,
  document.getElementById('root')
);



1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?