Open In App

Create a Crossword Puzzle Game using React-Native

Last Updated : 14 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Crossword Puzzle is a traditional word power game. The user needs to guess words based on the hints provided. So here, we will create a basic crossword puzzle app in React Native. The crossword puzzle app contains four buttons namely Generate, Verify, Reset and Solve. The Generate button will create a new crossword puzzle, the Verify button will check the correctness of the solved puzzle, the Reset button will empty all the fields entered in the puzzle and the Solve button will solve the puzzle.

Preview of the Final Output: Let us have a look at how the final application will look like.

Crossword

Crossword Puzzle App

Prerequisites

Approach to create Crossword Puzzle Game

Our code generates a random crossword puzzle. Here I have created our crossword puzzle with two levels. Each level has 4 words. These words need to be filled in the cells. Hints are given for each word. The user must guess the word and fill it in the cells based on the hint. I have used a list named crosswordData to store the words and hints. You can add more words to increase the levels in the game. Finally, the app provides good crossword puzzles to improve your word power.

Steps to create our Crossword Puzzle App

Step 1: Create a React Native app by using this command:

npx create-expo-app CrosswordPuzzle

Step 2: Navigate to our project through this command:

cd CrosswordPuzzle

Project Structure:

Project-Structure

Project Structure

The updated dependencies in package.json file will look like:

"dependencies": {
"react-native-paper": "4.9.2",
"@expo/vector-icons": "^13.0.0"
}

Example: Add the following code in CrosswordGrid.js and App.js.

Javascript




//CrosswordGrid.js
 
import React, { useState, useEffect } from 'react';
import { View, TextInput, StyleSheet, Text, Button } from 'react-native';
 
let level = 0;
 
const generateInitialGrid = (crosswordData) => {
    const initialGrid = Array(7).fill(0).map(() => Array(8).fill('X'));
    crosswordData[level].forEach(({ answer, startx, starty, orientation }) => {
        let x = startx - 1;
        let y = starty - 1;
 
        for (let i = 0; i < answer.length; i++) {
            if (orientation === 'across') {
                initialGrid[y][x + i] = '';
            } else if (orientation === 'down') {
                initialGrid[y + i][x] = '';
            }
        }
    });
    return initialGrid;
};
 
const generateAnswerGrid = (crosswordData) => {
    const answerGrid = Array(7).fill(0).map(() => Array(8).fill('X'));
    crosswordData[level].forEach(({ answer, startx, starty, orientation }) => {
        let x = startx - 1;
        let y = starty - 1;
 
        for (let i = 0; i < answer.length; i++) {
            if (orientation === 'across') {
                answerGrid[y][x + i] = answer[i];
            } else if (orientation === 'down') {
                answerGrid[y + i][x] = answer[i];
            }
        }
    });
    return answerGrid;
};
 
 
const CrosswordGrid = ({ crosswordData }) => {
    const [grid, setGrid] = useState(generateInitialGrid(crosswordData));
 
    useEffect(() => {
        setGrid(generateInitialGrid(crosswordData));
    }, [crosswordData]);
 
    const handleInputChange = (row, col, text) => {
        const newGrid = [...grid];
        newGrid[row][col] = text.toUpperCase();
        setGrid(newGrid);
    };
 
    const handleGenerate = () => {
        level = (level + 1) % 2;
        setGrid(generateInitialGrid(crosswordData));
    };
 
    const handleVerify = () => {
        const answerGrid = generateAnswerGrid(crosswordData);
        const isCorrect = JSON.stringify(grid) === JSON.stringify(answerGrid);
        if (isCorrect) {
            alert('Congratulations! Your crossword is correct.');
        } else {
            alert('Incorrect. Please try again.');
        }
    };
 
    const handleReset = () => {
        setGrid(generateInitialGrid(crosswordData));
    };
 
    const handleSolve = () => {
        const answerGrid = generateAnswerGrid(crosswordData);
        setGrid(answerGrid);
    };
 
    const renderGrid = () => (
        <View>
            {grid.map((row, rowIndex) => (
                <View key={rowIndex} style={styles.row}>
                    {row.map((cell, colIndex) => (
                        <View key={colIndex} style={styles.cellContainer}>
                            {crosswordData[level].map((entry) => {
                                const { startx, starty, position } = entry;
                                if (rowIndex + 1 === starty && colIndex + 1 === startx) {
                                    return (
                                        <Text key={`digit-${position}`}
                                              style={styles.smallDigit}>
                                            {position}
                                        </Text>
                                    );
                                }
                                return null;
                            })}
                            <TextInput
                                style={[styles.cell,
                                grid[rowIndex][colIndex] ==='X' ? styles.staticCell:null]}
                                value={cell}
                                editable={grid[rowIndex][colIndex] !== 'X'}
                                onChangeText={(text) =>
                                    handleInputChange(rowIndex,colIndex, text)
                                }
                                maxLength={1}
                            />
                        </View>
                    ))}
                </View>
            ))}
        </View>
    );
 
    const renderQuestions = () => {
        const questions = { across: [], down: [] };
 
        crosswordData[level].forEach(({ hint, orientation, position }) => {
            const questionText = `${position}. ${hint}`;
            questions[orientation].push(
                <Text key={`question-${position}`} style={styles.questionText}>
                    {questionText}
                </Text>
            );
        });
 
        return (
            <View>
                <View style={styles.headingContainer}>
                    <Text style={styles.headingText}>Across</Text>
                </View>
                <View style={styles.questionsContainer}>
                    {questions.across.map((question, index) => (
                        <View key={`across-question-container-${index}`}>
                            {question}
                        </View>
                    ))}
                </View>
                <View style={styles.headingContainer}>
                    <Text style={styles.headingText}>Down</Text>
                </View>
                <View style={styles.questionsContainer}>
                    {questions.down.map((question, index) => (
                        <View key={`down-question-container-${index}`}>
                            {question}
                        </View>
                    ))}
                </View>
            </View>
        );
    };
 
 
    return (
        <View style={styles.container}>
            {renderQuestions()}
            {renderGrid()}
            <View style={styles.buttonContainer}>
                <Button color={'#228B22'}
                        title="Generate"
                        onPress={handleGenerate}
                        style={styles.button} />
                <View style={styles.gap} />
                <Button color={'#228B22'}
                        title="Verify"
                        onPress={handleVerify}
                        style={styles.button} />
                <View style={styles.gap} />
                <Button color={'#228B22'}
                        title="Reset"
                        onPress={handleReset}
                        style={styles.button} />
                <View style={styles.gap} />
                <Button color={'#228B22'}
                        title="Solve"
                        onPress={handleSolve}
                        style={styles.button} />
            </View>
        </View>
    );
};
 
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    row: {
        flexDirection: 'row',
    },
    cellContainer: {
        position: 'relative',
    },
    cell: {
        borderWidth: 1,
        margin: 1,
        borderColor: '#228B22',
        width: 30,
        height: 30,
        textAlign: 'center',
    },
    staticCell: {
        borderColor: 'transparent',
        color: 'white',
    },
    smallDigit: {
        position: 'absolute',
        top: 2,
        left: 2,
        fontSize: 10,
        fontWeight: 'bold',
    },
    questionsContainer: {
        justifyContent: 'space-between',
        marginBottom: 10,
        padding: 10,
    },
    questionText: {
        fontSize: 16,
        fontStyle: 'italic',
    },
    headingContainer: {
        marginTop: 10,
        marginBottom: 5,
    },
    headingText: {
        fontSize: 18,
        fontWeight: 'bold',
        color: '#228B22',
        textAlign: 'center',
    },
    buttonContainer: {
        flexDirection: 'row',
        justifyContent: 'space-around',
        marginTop: 20,
        marginHorizontal: 10,
    },
    button: {
        flex: 1, // Ensure equal width for both buttons
    },
    gap: {
        width: 10, // Adjust the width as needed for the desired gap
    },
});
 
export default CrosswordGrid


Javascript




//App.js
 
import React from "react";
import { View, StyleSheet } from "react-native";
import CrosswordGrid from "./CrosswordGrid";
 
const App = () => {
    // levels can be added here in the crosswordData
    const crosswordData = [
        [
            {
                answer: "TIGER",
                hint: "The powerful predator roams the jungle",
                startx: 4,
                starty: 1,
                orientation: "down",
                position: 1,
            },
            {
                answer: "EAGLE",
                hint: "A majestic bird known for its keen eyesight",
                startx: 4,
                starty: 4,
                orientation: "across",
                position: 2,
            },
            {
                answer: "ITALIC",
                hint: "It's a style of typeface characterized by slanted letters",
                startx: 7,
                starty: 1,
                orientation: "down",
                position: 3,
            },
            {
                answer: "INFINITE",
                hint:"It describes something boundless or limitless in extent or quantity",
                startx: 1,
                starty: 2,
                orientation: "across",
                position: 4,
            },
        ],
        [
            {
                answer: "QUIVER",
                hint: "To shake or tremble slightly, often with rapid movements",
                startx: 1,
                starty: 4,
                orientation: "across",
                position: 1,
            },
            {
                answer: "TWIRL",
                hint: "To spin or rotate quickly",
                startx: 3,
                starty: 2,
                orientation: "down",
                position: 2,
            },
            {
                answer: "GAZE",
                hint: "To look steadily and intently at something,
                        often implying concentration or contemplation",
                startx: 5,
                starty: 1,
                orientation: "down",
                position: 3,
            },
            {
                answer: "FLUTE",
                hint: "A musical instrument with a high-pitched sound",
                startx: 2,
                starty: 6,
                orientation: "across",
                position: 4,
            },
        ],
    ];
 
    return (
        <View style={styles.container}>
            <CrosswordGrid crosswordData={crosswordData} />
        </View>
    );
};
 
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
    },
});
 
export default App;


Step to run the application:

Step 1: Open the terminal and enter the following command to run the app

npx expo start

Step 2: Depending on your Operating System, type the following command.

  • To run on Android:
npx react-native run-android
  • To run on Ios:
npx react-native run-ios

Output:

CrosswordPuzzle

Crossword Puzzle App



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads