运行效果
选择难度
错误数字提示
选择数字相同数字提示
游戏完成提示(演示效果)
1.项目结构
import { message, Modal, Button, Select} from 'antd'
2.代码
App.js
import './App.css' ;
import Sudoku from "./components/sudoku/Sudoku" ;
function App ( ) {
return (
< div className= "App" >
< Sudoku/ >
< / div>
) ;
}
export default App;
sudoku/sudoku.jsx
import React, { Component} from "react" ;
import './index.css'
import { block_contains, column_contains, init, row_contains} from "./init" ;
import { message, Modal, Button, Select} from 'antd'
const { Option} = Select
const levels = [ '入门' , '简单' , '中等' , '困难' , ]
const classes = [ 'cellClick' , 'wrongNumber' , 'sameNumber' ]
const STANDARD_ARR = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]
let timeInterval
function init_game ( level ) {
let arr = init ( level)
for ( let i = 0 ; i < arr. length; i++ ) {
for ( let j = 0 ; j < arr[ i] . length; j++ ) {
arr[ i] [ j] = { value : arr[ i] [ j] , disabled : arr[ i] [ j] !== undefined }
}
}
return arr
}
function findIndex ( target, arr ) {
for ( let i = 0 ; i < arr. length; i++ ) {
if ( arr[ i] === target)
return i
}
return undefined
}
function setError ( target, error ) {
if ( error) {
target. classList. add ( classes[ 1 ] )
message. error ( '填的数字重复了' )
} else {
target. classList. remove ( classes[ 1 ] )
}
}
function computeTime ( time ) {
let sec = Math. floor ( time % 60 )
let min = Math. floor ( time / 60 )
return ` ${ min} : ${ sec} `
}
export default class Sudoku extends Component {
state = {
result : [ ] ,
choose : { i : - 1 , j : - 1 } ,
hoverTarget : undefined ,
showModal : false ,
level : 1 ,
startTime : 0 ,
}
constructor ( props ) {
super ( props) ;
this . table = React. createRef ( )
}
init ( ) {
this . setState ( { result : init_game ( this . state. level) , startTime : 0 } )
clearInterval ( timeInterval)
timeInterval = this . newInterval ( )
}
newInterval ( ) {
return setInterval ( ( ) => {
let startTime = this . state. startTime + 1
this . setState ( { startTime} )
} , 1000 )
}
componentDidMount ( ) {
this . init ( )
}
componentWillUnmount ( ) {
clearInterval ( timeInterval)
}
setGrandsonClass ( target, num, i, j ) {
let grandParent = this . table. current
if ( target) {
for ( let k = 0 ; k < grandParent. childNodes. length; k++ ) {
for ( let l = 0 ; l < grandParent. childNodes[ k] . childNodes. length; l++ ) {
let temp = grandParent. childNodes[ k] . childNodes[ l] . childNodes[ 0 ]
let number = temp. innerText
if ( number !== '' && num === number) temp. classList. add ( classes[ 2 ] )
else temp. classList. remove ( classes[ 2 ] )
if ( i === k && j === l) temp. classList. add ( classes[ 0 ] )
else temp. classList. remove ( classes[ 0 ] )
}
}
} else {
for ( let k = 0 ; k < grandParent. childNodes. length; k++ ) {
for ( let l = 0 ; l < grandParent. childNodes[ k] . childNodes. length; l++ ) {
let temp = grandParent. childNodes[ k] . childNodes[ l] . childNodes[ 0 ]
temp. classList. remove ( ... classes)
}
}
}
}
clickEle ( e ) {
if ( e. target. nodeName === 'SPAN' ) {
let target = e. target. parentNode, parent = target. parentNode, grandParent = parent. parentNode
let i = findIndex ( parent, grandParent. childNodes)
let j = findIndex ( target, parent. childNodes)
this . setState ( { choose : { i, j} } )
this . setGrandsonClass ( target, e. target. innerText, i, j)
}
}
chooseNum ( e ) {
let { i, j} = this . state. choose
if ( i !== - 1 || j !== - 1 ) {
let num = Number ( e. target. innerText)
let result = this . state. result
if ( result[ i] [ j] . disabled) {
message. warning ( '这个数字不可更改' )
return
}
let target = this . table. current. childNodes[ i] . childNodes[ j] . childNodes[ 0 ]
if ( row_contains ( result[ i] , num) || column_contains ( result, j, num) || block_contains ( result, i, j, num) ) {
setError ( target, true )
} else {
setError ( target, false )
}
result[ i] [ j] . value = num
this . setState ( { result} )
this . check ( )
}
}
clear ( ) {
let result = this . state. result
let { i, j} = this . state. choose
if ( result[ i] [ j] . value && ! result[ i] [ j] . disabled) {
result[ i] [ j] . value = undefined
this . setState ( { result} )
}
}
check ( ) {
let result = this . state. result
let hasBlank = false
for ( let i = 0 ; i < result. length; i++ ) {
for ( let j = 0 ; j < result[ i] . length; j++ ) {
if ( result[ i] [ j] . value === undefined ) {
hasBlank = true
return
}
}
}
if ( ! hasBlank) {
this . setState ( { showModal : true } )
clearInterval ( timeInterval)
message. success ( '恭喜完成!' )
}
}
nextGame ( ) {
this . setState ( { showModal : false } )
this . init ( )
this . setGrandsonClass ( )
}
nextGameCancel ( ) {
this . setState ( { showModal : false } )
timeInterval = this . newInterval ( )
}
chooseLevel ( e ) {
this . setState ( { level : e} )
}
render ( ) {
return ( < div>
< Select placeholder= "难度" onChange= { this . chooseLevel . bind ( this ) }
className= "chooseLevel" >
< Option value= { 1 } > { levels[ 0 ] } < / Option>
< Option value= { 2 } > { levels[ 1 ] } < / Option>
< Option value= { 3 } > { levels[ 2 ] } < / Option>
< Option value= { 4 } > { levels[ 3 ] } < / Option>
< / Select>
< Button onClick= { this . clear . bind ( this ) } className= "clickButton clear" > 清除< / Button>
< Button onClick= { this . init . bind ( this ) } className= "clickButton reset" > 重置< / Button>
< div className= "clickButton timeUsed" > { computeTime ( this . state. startTime) } < / div>
< div className= "table" ref= { this . table} onClick= { this . clickEle . bind ( this ) } >
{ this . state. result. map ( ( row_item, row_index ) => {
return (
< div className= { row_index % 3 === 0 && row_index > 0 ? 'row line' : 'row' }
key= { row_index} >
{ row_item. map ( ( cell_item, cell_index ) => {
return (
< div className= { cell_index % 3 === 0 && cell_index > 0 ? 'cell column' : 'cell' }
key= { cell_index} >
< span className= "content" > { cell_item. value} < / span>
< / div> )
} ) }
< / div> )
} ) }
< / div>
< div id= "numbers" >
{ STANDARD_ARR . map ( item => {
return ( < span key= { item} className= "chooseNumber"
onClick= { this . chooseNum . bind ( this ) } >
{ item}
< / span> )
} ) }
< / div>
< Modal open= { this . state. showModal}
title= "游戏完成"
onOk= { this . nextGame . bind ( this ) }
onCancel= { this . nextGameCancel . bind ( this ) }
cancelText= "取消"
okText= "重新开始" >
< span> 恭喜完成游戏,用时 { computeTime ( this . state. startTime) } , 开始下一局吧< / span>
< / Modal>
< / div> )
}
}
sudoku/index.css
.table {
width : 542px;
height : 542px;
margin : 30px auto 20px;
border : 1px #a5a5a5 solid;
border-radius : 4px;
}
.row {
height : 60px;
}
.row.line {
border-top : black 2px solid;
}
.cell.column {
border-left : black 2px solid;
}
.cell {
display : inline-block;
border : 1px #a5a5a5 solid;
border-radius : 4px;
}
.content {
display : block;
width : 58px;
height : 58px;
line-height : 58px;
font-size : 26px;
float : left;
}
.wrongNumber {
background : lightcoral !important ;
}
.sameNumber {
background : lightgray;
}
.cellClick {
background : #a5a5a5;
}
.chooseNumber {
padding : 5px 10px;
margin : 10px;
font : 26px bolder 黑体;
width : 30px;
height : 30px;
background : lightgray;
border-radius : 3px;
}
.chooseNumber:hover {
background : #a5a5a5;
}
.clickButton {
position : fixed;
right : 120px;
font-size : 20px;
padding : 5px 10px;
color : black;
background : #fbf9f9;
border-radius : 8px;
box-shadow : lightgray 4px 4px 4px;
border : lightgray 1px solid;
width : 100px;
height : 40px;
}
.clickButton:hover {
background : dodgerblue;
color : white !important ;
}
.chooseLevel {
position : fixed;
top : 200px;
right : 120px;
border-radius : 8px;
box-shadow : lightgray 4px 4px 4px;
border : lightgray 1px solid;
width : 100px;
}
.reset {
top : 100px;
}
.clear {
top : 50px;
}
.timeUsed {
top : 150px;
width : 78px;
height : 28px;
}
sudoku/init.js
function nextNumber ( num ) {
num += 1 ;
num = num > 9 ? 1 : num;
return num;
}
function generateRow ( ) {
let arr = [ ]
for ( let i = 0 ; i < 9 ; i++ ) {
arr[ i] = i + 1 ;
}
return arr
}
export const row_contains = ( arr, num ) => {
if ( arr == null ) {
return false ;
}
for ( let j of arr) {
if ( j && j. value === num) {
return true ;
}
}
return false ;
}
export const column_contains = ( arr, column, num ) => {
for ( let j of arr) {
if ( ! j[ column] ) {
return false
}
if ( j[ column] . value === num) {
return true ;
}
}
return false ;
}
export const block_contains = ( arr, i, j, num ) => {
let row = Math. floor ( i / 3 ) * 3 ;
let column = Math. floor ( j / 3 ) * 3
let temp = [ ] ;
for ( let k = row; k < row + 3 ; k++ ) {
for ( let l = column; l < column + 3 ; l++ ) {
if ( arr[ k] && arr[ k] [ l] ) temp[ ( k - row) * 3 + ( l - column) ] = { value : arr[ k] [ l] } ;
}
}
return row_contains ( temp, num) ;
}
function switchRow ( arr, i, j ) {
for ( let k = 0 ; k < arr[ i] . length; k++ ) {
let temp = arr[ i] [ k] ;
arr[ i] [ k] = arr[ j] [ k] ;
arr[ j] [ k] = temp;
}
}
function switchColumn ( arr, i, j ) {
for ( let k = 0 ; k < arr. length; k++ ) {
let temp = arr[ k] [ i] ;
arr[ k] [ i] = arr[ k] [ j] ;
arr[ k] [ j] = temp;
}
}
function random ( seeds ) {
return Math. ceil ( Math. random ( ) * seeds)
}
function switchRowAndColumn ( arr ) {
let times = random ( 40 ) + 10 ;
for ( let i = 0 ; i < times; i++ ) {
{
let j = random ( 3 ) - 1 ;
let k = random ( 3 ) - 1 ;
if ( j !== k) switchRow ( arr, j, k) ;
}
{
let j = random ( 3 ) - 1 ;
let k = random ( 3 ) - 1 ;
if ( j !== k) switchRow ( arr, j + 3 , k + 3 ) ;
}
{
let j = random ( 3 ) - 1 ;
let k = random ( 3 ) - 1 ;
if ( j !== k) switchRow ( arr, j + 6 , k + 6 ) ;
}
{
let j = random ( 3 ) - 1 ;
let k = random ( 3 ) - 1 ;
if ( j !== k) switchColumn ( arr, j, k) ;
}
{
let j = random ( 3 ) - 1 ;
let k = random ( 3 ) - 1 ;
if ( j !== k) switchColumn ( arr, j + 3 , k + 3 ) ;
}
{
let j = random ( 3 ) - 1 ;
let k = random ( 3 ) - 1 ;
if ( j !== k) switchColumn ( arr, j + 6 , k + 6 ) ;
}
}
}
function clearCells ( level, arr ) {
let min, max;
switch ( level) {
case 1 :
min = 20 ;
max = 10 ;
break ;
case 2 :
min = 30 ;
max = 10 ;
break ;
case 3 :
min = 35 ;
max = 10 ;
break ;
default :
min = 45 ;
max = 5 ;
break ;
}
let count = random ( max) + min
for ( let i = 0 ; i < count; i++ ) {
do {
let n = random ( 9 ) - 1 ;
let m = random ( 9 ) - 1 ;
if ( arr[ n] [ m] > 0 ) {
arr[ n] [ m] = undefined
break ;
}
} while ( true ) ;
}
}
export const init = ( level ) => {
let result = [ ] ;
result[ 0 ] = generateRow ( ) ;
for ( let i = 1 ; i < 9 ; i++ ) {
result. push ( [ ] )
for ( let j = 0 ; j < 9 ; j++ ) {
let number = nextNumber ( result[ i - 1 ] [ j] ) ;
while ( row_contains ( result[ i] , number) || column_contains ( result, j, number) || block_contains ( result, i, j, number) ) {
number = nextNumber ( number) ;
}
result[ i] [ j] = number;
}
}
switchRowAndColumn ( result) ;
clearCells ( level, result) ;
return result;
}