Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tour of Go exercise #23: rot13Reader

Tags:

go

I'm trying to solve the Tour of Go exercise rot13Reader:

Here is my solution:

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

func rot13(x byte) byte {
    switch {
    case x >= 65 && x <= 77:
        fallthrough
    case x >= 97 && x <= 109:
        x = x + 13
    case x >= 78 && x <= 90:
        fallthrough
    case x >= 110 && x >= 122:
        x = x - 13
    }
    return x
}

func (r13 *rot13Reader) Read(b []byte) (int, error) {
    n, err := r13.r.Read(b)
    for i := 0; i <= n; i++ {
        b[i] = rot13(b[i])
    }
    return n, err
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

It returns You prnpxrq tur poqr!, that means only the first word of "Lbh penpxrq gur pbqr!" is cracked. How can I crack the whole sentence?

like image 216
Pauli Avatar asked Jul 28 '15 06:07

Pauli


2 Answers

EDIT:

Basically your solution is good and works, you just mistyped 1 character:

case x >= 110 && x >= 122:

Change it to:

case x >= 110 && x <= 122:

Your input and output:

Lbh penpxrq gur pbqr!
You prnpxrq tur poqr!

There is change in every word. The problem is not that only the first word is read and decoded, the problem is in your decoding algorithm.

In ROT13 if shifting goes outside of the letter range, you have to start from the beginning of the alphabet (or at the end). For example shifting 'N' would be 'Z' + 1, so it becomes 'A', the first letter. See this simple character mapping:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm

So what you should do is after shifting by 13, if the letter goes outside of the alphabet, shift it by -26 (number of letters in the English alphabet) which has the desired effect (that after the last letter you continued from the first).

An example solution:

func rot13(x byte) byte {
    capital := x >= 'A' && x <= 'Z'
    if !capital && (x < 'a' || x > 'z') {
        return x // Not a letter
    }

    x += 13
    if capital && x > 'Z' || !capital && x > 'z' {
        x -= 26
    }
    return x
}

And its output:

You cracked the code!

Try it on the Go Playground.

like image 166
icza Avatar answered Sep 28 '22 05:09

icza


You could also use instead:

func rot13(x byte) byte {
    input := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
    output := []byte("NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm")
    match := bytes.Index(input, []byte{x})
    if match == -1 {
        return x
    }
    return output[match]
}
like image 22
iDuran Avatar answered Sep 28 '22 06:09

iDuran