Alphabet mathematics

Shefki's Entry

Winner

Very clean and well-documented

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
-- remove all entries from a table 
local function wipe(t)
    for k in pairs(t) do
        t[k] = nil
    end
end

-- Find if any of the constraints make the set invalid.
-- In particular if there are letters with the same value
-- or leading letters with the value zero.
local tmp = {}
local function is_set_valid(letters, leading)
    wipe(tmp)
    for k, v in pairs(letters) do
        if letters[k] == 0 and leading[k] then
            -- Leading digits can't be zero
            return false 
        end
        if not tmp[v] then
            tmp[v] = true
        else
            -- Found a duplicate.
            return false 
        end
    end
    return true
end

-- Recursive iterator to move the letters table to the
-- next set of values.  Returns true if the letters 
-- table is set at the next state or nil if the
-- table has already reached the end.
local function next_set(letters, leading, index)
    index = next(letters, index)
    if not index then
        -- hit the end
        return
    end

    local cur = letters[index]
    if cur == 9 then -- roll over to next character and reset to our lowest 
        letters[index] = leading[index] and 1 or 0
        return next_set(letters, leading, index)
    else
        cur = cur + 1
        letters[index] = cur
        return true
    end
end

-- Parses the str to setup the letters and leading tables
-- for the iterator.  The values for the letters may or may
-- not be in a valid state under the constraints.
local function setup_state(str, letters, leading)
    for i = 1, #str do
        local letter = str:sub(i, i)
        if not tonumber(letter) then
            if i == 1 then
                leading[letter] = true
                letters[letter] = 1
            else
                letters[letter] = 0
            end
        end
    end
end

-- Replace the letters in the letters table with their values
-- in left right and answer
local function substitute(letters, left, right, answer)
    for k, v in pairs(letters) do
        left = left:gsub(k, v)
        right = right:gsub(k, v)
        answer = answer:gsub(k, v)
    end
    return tonumber(left), tonumber(right), tonumber(answer)
end

-- operation table of the functions to do the actual math.  Answers
-- are placed in the answers table passed in under the key of the
-- operation.
local op = {
    ['+'] = function(left, right, answers) answers['+'] = left + right end,
    ['-'] = function(left, right, answers) answers['-'] = left - right end,
    ['*'] = function(left, right, answers) answers['*'] = left * right end,
    ['^'] = function(left, right, answers) answers['^'] = left ^ right end,
}
op['?'] = function(left, right, answers)
    op['+'](left, right, answers)
    op['-'](left, right, answers)
    op['*'](left, right, answers)
    op['^'](left, right, answers)
end

-- helper function for the table sort
local function sort_results(a, b)
    if not a then
        return false
    elseif not b then
        return true
    end

    local a_left, b_left = a[1], b[1]
    if a_left ~= b_left then
        return a_left < b_left
    end

    local a_op, b_op = a[2], b[2]
    if a_op ~= b_op then
        return a_op < b_op
    end

    local a_right, b_right = a[3], b[3]
    if a_right ~= b_right then
        return a_right < b_right
    end
end

-- Iterate the results and print them
local function print_results(results)
    for i = 1, #results do
        local entry = results[i]
        print(entry[1] .. ' ' .. entry[2] .. ' ' .. entry[3] .. ' = ' .. entry[4])
    end
end

-- Takes left, operation, right and answer which can have letters in them and finds
-- the values for the letters which make the expression that they represent true.
-- operation can be + - * ^ or ?.  ? executes is all of the other operators ( + - * ^).
-- Output is printed to standard output in a sorted order.
function calculate(left, operation, right, answer)
    local results, letters, leading = {}, {}, {}
    setup_state(left, letters, leading)
    setup_state(right, letters, leading)
    setup_state(answer, letters, leading)
    repeat    
        if is_set_valid(letters, leading) then
            local answers = {}
            local s_left, s_right, s_answer = substitute(letters, left, right, answer)
            op[operation](s_left, s_right, answers)
            for op, answer in pairs(answers) do
                if answer == s_answer then
                    results[#results+1] = { s_left, op, s_right, s_answer }
                end
            end
        end
    until not next_set(letters, leading)
    table.sort(results, sort_results)
    print_results(results)
end

Facts

Date created
13 Jun 2009
Updated
13 Jun 2009

Contestant