Lock Challenge in Alloy

我只是一个虾纸丫 提交于 2021-01-07 06:41:23

问题


I would like to solve the following lock challenge using Alloy.

My main issue is how to model the integers representing the digit keys.

I created a quick draft:

sig Digit, Position{}

sig Lock {
 d: Digit one -> lone Position
}

run {} for exactly 1 Lock, exactly 3 Position, 10 Digit

In this context, could you please:

  • tell me if Alloy seems to you suitable to solve this kind of problem?
  • give me some pointers regarding the way I could model the key digits (without using Ints)?

Thank you.


回答1:


Yes, I think Alloy is suitable for this kind of problem.

Regarding digits, you don't need integers at all: in fact, it is a bit irrelevant for this particular purpose if they are digits or any set of 10 different identifiers (no arithmetic is performed with them). You can use singleton signatures to declare the digits, all extending signature Digit, which should be marked as abstract. Something like:

abstract sig Digit {}
one sig Zero, One, ..., Nine extends Digit {}

A similar strategy can be used to declare the three different positions of the lock. And btw since you have exactly one lock you can also declare Lock as singleton signature.




回答2:


My frame of this puzzle is:

enum Digit { N0,N1,N2,N3,N4,N5,N6,N7,N8,N9 }
one sig Code {a,b,c:Digit}

pred hint(h1,h2,h3:Digit, matched,wellPlaced:Int) {
    matched = #(XXXX)        // fix XXXX
    wellPlaced = #(XXXX)     // fix XXXX
}

fact {
    hint[N6,N8,N2, 1,1]
    hint[N6,N1,N4, 1,0]
    hint[N2,N0,N6, 2,0]
    hint[N7,N3,N8, 0,0]
    hint[N7,N8,N0, 1,0]
}

run {}



回答3:


A simple way to get started, you do not always need sig's. The solution found is probably not the intended solution but that is because the requirements are ambiguous, took a shortcut.

pred lock[ a,b,c : Int ] {
    a=6 || b=8 || c= 2
    a in 1+4 || b in 6+4 || c in 6+1
    a in 0+6 || b in 2+6 || c in 2+0
    a != 7 && b != 3 && c != 8
    a = 7 || b=8 || c=0 
}

run lock for 6 int

Look in the Text view for the answer.

upate we had a discussion on the Alloy list and I'd like to amend my solution with a more readable version:

let sq[a,b,c]       = 0->a + 1->b + 2->c
let digit       = { n : Int | n>=0 and n <10 }

fun correct[ lck : seq digit, a, b, c : digit ] : Int    { # (Int.lck & (a+b+c)) }
fun wellPlaced[ lck : seq digit, a, b, c : digit ] : Int { # (lck & sq[a,b,c])   }

pred lock[ a, b, c : digit ] {
    let lck = sq[a,b,c] {
        1 = correct[ lck, 6,8,2] and 1 = wellPlaced[ lck, 6,8,2]        
        1 = correct[ lck, 6,1,4] and 0 = wellPlaced[ lck, 6,1,4]
        2 = correct[ lck, 2,0,6] and 0 = wellPlaced[ lck, 2,0,6]
        0 = correct[ lck, 7,3,8]
        1 = correct[ lck, 7,8,0] and 0 = wellPlaced[ lck, 7,8,0]
    }
}

run lock for 6 Int



回答4:


When you think solve complete, let's examine whether the solution is generic.

Here is another lock.
If you can’t solve this in same form, your solution may not enough.

  • Hint1: (1,2,3) - Nothing is correct.
  • Hint2: (4,5,6) - Nothing is correct.
  • Hint3: (7,8,9) - One number is correct but wrong placed.
  • Hint4: (9,0,0) - All numbers are correct, with one well placed.



回答5:


I like the Nomura solution on this page. I made a slight modification of the predicate and the fact to solve.

enum Digit { N0,N1,N2,N3,N4,N5,N6,N7,N8,N9 }
one sig Code {a,b,c: Digit}

pred hint(code: Code, d1,d2,d3: Digit, correct, wellPlaced:Int) {
    correct = #((code.a + code.b + code.c)&(d1 + d2 + d3))
    wellPlaced = #((0->code.a + 1->code.b + 2->code.c)&(0->d1 + 1->d2 + 2->d3))
}

fact {
    some code: Code | 
    hint[code, N6,N8,N2, 1,1] and
    hint[code, N6,N1,N4, 1,0] and
    hint[code, N2,N0,N6, 2,0] and
    hint[code, N7,N3,N8, 0,0] and
    hint[code, N7,N8,N0, 1,0]
}

run {}

Update (2020-12-29): The new puzzle presented by Nomura (https://stackoverflow.com/a/61022419/5005552) demonstrates a weakness in the original solution: it does not account for multiple uses of a digit within a code. A modification to the expression for "correct" fixes this. Intersect each guessed digit with the union of the digits from the passed code and sum them for the true cardinality. I encapsulated the matching in a function, which will return 0 or 1 for each digit.

enum Digit {N0,N1,N2,N3,N4,N5,N6,N7,N8,N9}

let sequence[a,b,c] = 0->a + 1->b + 2->c

one sig Code {c1, c2, c3: Digit}

fun match[code: Code, d: Digit]: Int { #((code.c1 + code.c2 + code.c3) & d) }

pred hint(code: Code, d1,d2,d3: Digit, correct, wellPlaced:Int) {   
    // The intersection of each guessed digit with the code (unordered) tells us 
    // whether any of the digits match each other and how many
    correct = match[code,d1].plus[match[code,d2]].plus[match[code,d3]]
    // The intersection of the sequences of digits (ordered) tells us whether 
    // any of the digits are correct AND in the right place in the sequence
    wellPlaced = #(sequence[code.c1,code.c2,code.c3] & sequence[d1, d2, d3])
}

pred originalLock {
    some code: Code | 
    hint[code, N6,N8,N2, 1,1] and
    hint[code, N6,N1,N4, 1,0] and
    hint[code, N2,N0,N6, 2,0] and
    hint[code, N7,N3,N8, 0,0] and
    hint[code, N7,N8,N0, 1,0]
}

pred newLock {
    some code: Code | 
    hint[code, N1,N2,N3, 0,0] and 
    hint[code, N4,N5,N6, 0,0] and
    hint[code, N7,N8,N9, 1,0] and
    hint[code, N9,N0,N0, 3,1]
}

run originalLock
run newLock
run test {some code: Code | hint[code, N9,N0,N0, 3,1]}


来源:https://stackoverflow.com/questions/60951163/lock-challenge-in-alloy

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!