1

I made vigenere cipher in react. everything works perfectly regarding vigenere cipher algorithm. there is just one thing i want to add. when i take one letter from text and one letter from password, i want correspongind row and column on the board to be colored one by one. for example: if password is cat and text is rat, first i want to light up coresponding row and column of c and r, than on the next step a and a, then t and t. thats it.

currently i have no idea how to use useState() in such a way to get every column and row colored on each step. i managed to color last row and column checked but thats not what i am looking for.

// import { useState } from 'react';
// import './App.css';

function VigenereCipher() {
  const georgianLetters = 'აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ';
  const table = Array.from({ length: 33 }, (_, i) =>
    Array.from({ length: 33 }, (_, j) => georgianLetters[(i + j) % 33])
  );
  const [password, setPassword] = React.useState('');
  const [text, setText] = React.useState('');
  const [ciphered, setCiphered] = React.useState('');
  const [row, setRow] = React.useState(null);
  const [col, setCol] = React.useState(null);
  
  const handlePasswordChange = (event) => {
    setPassword(event.target.value);
  };

  const handleTextChange = (event) => {
    setText(event.target.value);
  };

  const handleCipherButtonClick = () => {
    if (!password || !text) {
      setCiphered('');
      alert(`შეავსეთ პაროლის და ტექსტის ველები`);
      return;
    }
  
    let k = 0;
    let cipheredText = '';
  
    for (let i = 0; i < text.length; i++) {
      if (text[i] === ' ') {
        cipheredText += ' ';
      } else {
        const rowIndex = georgianLetters.indexOf(text[i]);
        const colIndex = georgianLetters.indexOf(password[k]);
        if (rowIndex !== -1 && colIndex !== -1) {
          cipheredText += table[rowIndex][colIndex];
          k = (k + 1) % password.length;
        }
      }
    }
  
    setCiphered(cipheredText);
  };

  return (
    <div className="main">
      <table>
        <tbody>
          {georgianLetters.split('').map((letter, i) => (
            <tr key={i} style={i === row ? {backgroundColor: 'red'}: {}}>{georgianLetters.split('').map((innerLetter, j) => (
                <td 
                  key={j} 
                  style={{
                    backgroundColor: i === row && j === col ? 'purple': j === col ? 'red': 'none',
                  }}
                >{table[i][j]}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <div className="right-side">
        <div className='pass-text'>
          <label>პაროლი</label>
          <input type="text" value={password} onChange={handlePasswordChange} />
          <label>ტექსტი</label>
          <textarea value={text} onChange={handleTextChange}></textarea>
        </div>

        <button onClick={handleCipherButtonClick}>დაშიფვრა</button>
        <div className="ciphered">
          <textarea readOnly value={ciphered}></textarea>
        </div>
      </div>
    </div>
  );
}

ReactDOM.render(<VigenereCipher/>, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
DoLee
  • 11
  • 2

1 Answers1

1

Edit

After the clarification in the comment it was apparent that I have misunderstood the assignment, therefore I'll update this answer with the proper solution.

This solution does only provide the display logic which you asked for, you would obviously still need to implement the Vigenere Cipher yourself.

function HighlightGrid({ twodimArray, renderFunc }) {
  return (
    <div
      style={{
        display: "grid",
        gridTemplateRows: `repeat(${twodimArray.length}, 1fr)`,
        gridTemplateColumns:
          twodimArray.length === 0
            ? `0`
            : `repeat(${twodimArray[0].length}, 1fr)`,
      }}
    >
      {twodimArray.map((row, rowIndex) =>
        row.map((cellValue, columnIndex) => (
          <div>{renderFunc(cellValue, rowIndex, columnIndex)}</div>
        ))
      )}
    </div>
  );
}

function Character({ value, backgroundColor = "transparent" }) {
  return (
    <span style={{ backgroundColor: backgroundColor }}>
      {value === undefined ? "" : value}
    </span>
  ); // you should use character ?? "" but SO does not let me do that here
}

function MyAlgorithmComponent() {
  const [someGrid, setSomeGrid] = React.useState([
    [1, 2, 3, 5, 6],
    [4, 2, 1, 6, 7],
    [9, 6, 0, 6, 8],
  ]);
  const [position, setPosition] = React.useState({ x: 0, y: 0 });

  React.useEffect(() => {
    myAlgorithm();
  }, []);

  function myAlgorithm() {
    // imitating some algorithm which sets the position after some computing
    setInterval(() => {
      const randomXPosition =
        someGrid.length === 0
          ? 0
          : Math.floor(someGrid[0].length * Math.random());
      const randomYPosition = Math.floor(someGrid.length * Math.random());
      setPosition({ x: randomXPosition, y: randomYPosition });
    }, 2000);
  }

  console.log(position);
  function renderGridCell(cellValue, rowIndex, columnIndex) {
    return rowIndex === position.y && columnIndex === position.x ? (
      <Character value={cellValue} backgroundColor="red" />
    ) : (
      <Character value={cellValue} />
    );
  }

  return <HighlightGrid twodimArray={someGrid} renderFunc={renderGridCell} />;
}

ReactDOM.render(<MyAlgorithmComponent />, document.getElementById("root"));
/* Do not let console output overlap HTML */
.as-console-wrapper {
    max-height: 5rem !important;
    bottom: 0;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

The solution comprises three components:

<Grid />

Responsible for displaying a two dimensional array as a grid.

  • Props:
    • twoDimArray: 2D Array which contents should be displayed
    • renderFunc: Function which return value determines what will be displayed in a cell. It receives the cellValue (from the twoDimArray) as well as its position indices.

<Character />

Responsible for displaying a single character. This is what should be displayed in each cell.

  • Props:
    • value: value/ character to display
    • backgroundColor: background color for the character, default value is transparent

<MyAlgorithmComponent />

This is the brains behind the operation. There the actual algorithm executes and it decides which what is being displayed in each cell. For that purpose it has the renderGridCell() function which renders a <Character /> with red background if the character should be highlighted or a <Character /> with default background otherwise.

To decide when a background should be highlighted the component has a state position which keeps track of the current x and y coordinates that should be highlighted.

Last but not least there is the actual algorithm myAlgorithm(). It is responsible to set the position according to the algorithm. For you that is where you would place the logic for the Viginere Cipher. I have some dummy implementation which sets the position to random spots within the grid, just to show how it would work.

Original answer

You can simple split() both words and render each character separately with a different background color.

const colors = [
    "orange",
    "green",
    "yellow",
    "red",
    "cyan"
]

function TextCompare({ x, y }) {
    const [wordOne, setWordOne] = React.useState(x);
    const [wordTwo, setWordTwo] = React.useState(y);

    return (
        <div>
            <div>
                {wordOne.split("").map((x, i) => <Character index={i} character={x}/>)}
            </div>
            <div>
                {wordTwo.split("").map((x, i) => <Character index={i} character={x}/>)}
            </div>
        </div>
    )
}

function Character({ index, character }) {
    return (<span style={{ backgroundColor: getColor(index) }}>{character ? character : ""}</span>); // you should use character ?? "" but SO does not let me do that here
}

function getColor(index) {
    return colors[index % colors.length];
}

ReactDOM.render(<TextCompare x="caterpillar" y="cat" />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

To generate nice colors indefinitely you could also use a generator function. I cannot use the generator function within the React snippet on StackOverflow, but you should be able to do that on your page. You then just need to instantiate two generators for each word:

// generator function
function* generateHslaColor(saturation, lightness, alpha, amount) {
    let huedelta = Math.trunc(360 / amount)
    let i = 0;
    while (true) {
        const hue = huedelta * (i++ % amount);
        yield `hsla(${hue},${saturation}%,${lightness}%,${alpha})`;
    }
}

// generate 20 numbers which repeat after 5 colors
const repeatAfter = 5
// instantiate generator (if you need the same color sequence twice, instantiate two with same parameters)
const colorGenerator = generateHslaColor(50, 100, 1.0, repeatAfter); 
console.log([...new Array(20)].map(x => colorGenerator.next().value));
/* Stackoverflow: show only console */
.as-console-wrapper {
    max-height: 100% !important;
    top: 0;
}
Mushroomator
  • 6,516
  • 1
  • 10
  • 27
  • thats not whay i am trying to achieve. imagine password is pet and word is cat. when i am deciphering with vigenere cipher i tace p on x axis and c on y axis and write down letter where this row and column meet. i want that row and column to color red. than when i move to next letter of password and text, i want to get a on x axis and e on y axis and color coresponding rows and columns. – DoLee Aug 05 '23 at 10:35
  • Oh ok, it seems there was a misunderstanding. I've updated my answer to reflect that. – Mushroomator Aug 05 '23 at 12:01
  • [link](https://drive.google.com/file/d/1L6whoM_Vb1nM-GI8fa7C6SYGPiA4soRP/view?usp=sharing) thats what i am looking for. i have provided my code where i have already implemented vigenere cipher and i just want to add function that will color corresponding rows and columns when ciphering. – DoLee Aug 05 '23 at 20:17
  • I'm just showing you the idea on how you would be able to display something like that, which is the question. And with what I've provided you should be able to do exactly what that picture shows with some smaller adjustments. I won't be writing the whole thing for you. – Mushroomator Aug 05 '23 at 20:41
  • thank you for your effort i appreciate that. i ended up getting to answer myself. i used rowIndex and colIndex variables to set row and col that i wanted to color. then i set delay to see all the renders and not just last one. everything works fine. where can i add my resolved code? – DoLee Aug 05 '23 at 21:04