-1

I am trying to make a game where a paragraph of text is “encrypted” using a simple substitution cipher, so for example, all A's will be F's and B's will be G's an so on. The idea is that the user/player will need to try to guess the famous quote by trying to decrypt the letters. So the screen shows them a blank space with a letter A and they have to figure out it's really an F that goes in the place within the string. I've not got very far, basically I can manually change each letter using a for loop, but there must be an easier way.

import java.util.Scanner;
import java.util.Random;

public class cryptogram {
    

    public static void main(String[] args) {
        
        char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
        
        for (char i = 0; i <alphabet.length; i++) {
            if (alphabet[i] == 'B') {
                    alphabet[i] = 'Z';
            }
        }
        System.out.println(alphabet);
    }
}
President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
  • Setup two arrays, one is the the original character, the other is the substituted character. Find the index of the character in the first array, then use this to look up the substitution array – MadProgrammer Feb 15 '22 at 23:00
  • You can also use a Map or BidiMap to look up the substituions. Btw: Your "A's will be F's and B's will be G's an so on" does not match what your code does. If you have a sequential substitution, you can just use math instead of a lookup data structure. – f1sh Feb 15 '22 at 23:07
  • @MadProgrammer thank you! Looks like the 2 arrays have got me further in the right direction – Leanne Graham Feb 15 '22 at 23:22
  • @f1sh appreciate your reply. I’ve not come across Map or BidiMap so I’ll read up on those. Thank you – Leanne Graham Feb 15 '22 at 23:24

1 Answers1

0

Substitution

A "substitution" workflow might look something like...

public class Main {

    public static void main(String[] args) {
        new Main().substitution();
    }
    
    public void substitution() {
        char[] lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ".toCharArray();
        char[] substitution = " FGHIJKLMNOPQRSTUVWXYZABCDE".toCharArray();
        
        String text = "This is a test";
        StringBuilder builder = new StringBuilder(text.length());
        for (char value : text.toCharArray()) {
            int index = indexOf(value, lookup);
            builder.append(substitution[index]);
        }
        
        String encypted = builder.toString();
        
        System.out.println(text);
        System.out.println(encypted);

        builder = new StringBuilder(text.length());
        for (char value : encypted.toCharArray()) {
            int index = indexOf(value, substitution);
            builder.append(lookup[index]);
        }
        System.out.println(builder.toString());
    }
    
    protected static int indexOf(char value, char[] array) {
        char check = Character.toUpperCase(value);
        for (int index = 0; index < array.length; index++) {
            if (check == array[index]) {
                return index;
            }
        }
        return -1;
    }
}

Which will output something like...

This is a test
XLMWEMWE EXIWX
THIS IS A TEST

Now, obviously, this only supports upper-cased characters and does not support other characters like numbers or punctuation (like ! for example). The above example will also crash if the character can't be encoded, it's just an example of an idea after all

A "different" approach

Now, char is a peculiar type, as it can actually be treated as an int. This has to do with how text is encoded by computers, see ASCII Table for an example.

This means that we can do mathematical operations on it (+/-). Now, assuming that we only want to deal with "displayable" characters, this gives us a basic range of 32-126 (you could also have the extended range from 128-255, but lets keep it simple for now)

With this is hand, we could actually do something like...

public class Main {

    public static void main(String[] args) {
        new Main().encode();
    }

    private static final int MIN_RANGE = 32;
    private static final int MAX_RANGE = 127;
    
    public void encode() {
        String text = "This is a test";
        String encoded = encode(text, 4);
        System.out.println(text);
        System.out.println(encoded);
        System.out.println(encode(encoded, -4));
    }
    
    protected String encode(String value, int offset) {
        StringBuilder sb = new StringBuilder(value.length());
        for (char c : value.toCharArray()) {
            sb.append(encode(c, offset));
        }
        return sb.toString();
    }
    
    protected char encode(char value, int offset) {
        char newValue = (char)(value + offset);
        if (newValue < MIN_RANGE) {
            newValue = (char)(MAX_RANGE - (MIN_RANGE - newValue));
        } else if (newValue > MAX_RANGE) {
            newValue = (char)((newValue - MAX_RANGE) + MIN_RANGE);
        }
        return newValue;
    }
}

Which outputs...

This is a test
Xlmw$mw$e$xiwx
This is a test

As you can see, decoding is just passing the encoded text with offset in the opposite direction. It's also easier to change the offset if you want to change the encoding process

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I understand. So my paragraph will have special characters so I think the int approach will be the one to take although your first solution is working for me too for shorter sentences where I’ve a string for each word just to practice. One day I hope to write code as fluently as you! Appreciate your time. – Leanne Graham Feb 16 '22 at 00:05
  • @LeanneGraham With the first approach, you need to build the array of characters you're willing to support, where as the second will, generally, be a better approach, assuming you're only dealing with "displayable" characters – MadProgrammer Feb 16 '22 at 00:18