Truth Tables: Your great-grandfather's computer

13

If you remember back to your schooling years, you might remember learning about Truth Tables. They seemed boring, but they are basis for logic and (some would argue) all computing...


Problem

Your mission, should you choose to accept it is to write a program, function, or widget of code that can output a truth table given input.

Input

Input will be a string (like data structure) containing the logic statement to make the Truth Table into. For example:

p ∧ q

This means p and q(logical conjuction) and will output:

 p  q  p ∧ q
 T  T    T
 T  F    F
 F  T    F
 F  F    F            

Notice the spacing: The item of the column is in the center of the header

Characters

Score via characters, not bytes The logic comparison characters are special and not always what they look like. Use these characters:

Logical Conjunction (AND): U+2227

Logical Disjunction (OR): U+2228

Logical Negation (NOT) ~ or ¬ U+7e and U+ac respectively


Bonuses

All of these bonuses are optional, but will knock points off your score. Pick any.

Logical Negation

Logical Negation is a unary operator in truth tables. It is the equivalent of ! in most C-based languages. It makes false=>true and vise versa. It is notated with a ¬ or ~ (you must support both). Supporting this will knock off 10% of your score. You must, however, add an additional column to show its results: For example:

~p ∧ q

will output:

p  ~p  q  ~p ∧ q
T  F   T     F
T  F   F     F
F  T   T     T
F  T   F     F

Pretty Print

The normal table notation is boring. Let's make it pretty! Pretty print format is as follows for p ∧ q is as follows:

+---+---+-------+
| p | q | p ∧ q |
+---+---+-------+
| T | T |   T   |
+---+---+-------+
| T | F |   F   |
+---+---+-------+
| F | T |   F   |
+---+---+-------+
| F | F |   F   |
+---+---+-------+

Special details for pretty printing:

  • There is a 1 space padding in each cell
  • Cell values are still centered

If you pretty print your tables, from your code and then multiply by 0.6. Use this function for this bonus:

score = 0.6 * code

Examples

p ∧ q:

p  q  p ∧ q
T  T    T
T  F    F
F  T    F
F  F    F

p ∨ q:

p  q  p ∨ q
T  T    T
T  F    T
F  T    T
F  F    F

~p ∧ q:

p  ~p  q  ~p ∧ q
T   F  T     F
T   F  F     F
F   T  T     T
F   T  F     F

~p ∨ q:

p  ~p  q  ~p ∧ q
T   F  T     T
T   F  F     F
F   T  T     T
F   T  F     T

Rules

  • Standard loopholes apply
  • No external resources
  • If you're going to break the rules, be clever ;)

Shortest Code (in characters) wins. Good Luck!

MayorMonty

Posted 2015-09-18T02:23:11.943

Reputation: 778

4From the description it sounded like these were arbitrary Boolean expressions. But all the examples (without bonus) have only one operator. Is this restricted to a single operator? Also, the names of the values in the examples are all p and q. Unless they always have these names, you may want to show a few different options in the test examples. Are they always a single letter? – Reto Koradi – 2015-09-18T03:44:40.893

2Since this uses non-ASCII characters, it might be good to specify if the length of the code is counted in characters or bytes. If it's bytes, it would be helpful to know how many bytes the unicode characters use. – Reto Koradi – 2015-09-18T03:51:38.147

Simplify :). score = 0.6 * (code - 15) = .6 * code - 9 – mınxomaτ – 2015-09-18T07:14:01.653

@RetoKoradi Changed. Score by characters, not bytes – MayorMonty – 2015-09-19T04:35:19.483

@RetoKoradi If what my geometry teacher tells me is correct you will never see more then p q and r in a truth table ;) – MayorMonty – 2015-09-19T04:36:12.597

What?! No way! I was just thinking of making a challenge like this! – mbomb007 – 2015-09-19T05:15:46.627

@SpeedyNinja You can easily see more than those in a truth table. You might not see it in a class, but that's like saying that you'll never see an equation with more than three variables. – mbomb007 – 2015-09-19T05:19:13.413

@mbomb007 For the purposes of keeping a moderately difficult challenge simple, if an answer doesn't support more then p q or r that will be fine – MayorMonty – 2015-09-19T05:22:38.387

I would like to state for the record that my great-grandfather's computer was a slide rule. I doubt that anyone's great-grandfather computed with truth tables: the jump was directly from slide rules to electronic computers. – Peter Taylor – 2016-01-09T17:19:58.873

May we pretty-print using box-drawing chars ┌─┬? – Adám – 2016-05-31T06:06:47.657

Does a symbolic expression count as a string-like data structure; i.e. can I take my input as P∨Q or does it have to be "P∨Q"? – ngenisis – 2017-01-11T01:00:01.183

May we take just the Boolean operator (∧ ∨ ~) instead of the full "p ∧ q" string? – Adám – 2017-01-11T09:41:07.193

@Adám String parsing is not the challenge, you may take the operator as input – MayorMonty – 2017-01-11T17:33:52.150

Can we assume that 'p', 'q', etc. is just one character long? – 0WJYxW9FMN – 2017-01-11T20:44:16.667

Is each boolean (like p and q) necessarily in alphabetical order in the table? – 0WJYxW9FMN – 2017-01-11T20:48:36.673

Answers

6

JavaScript (ES6), 141

Simple function, no bonus, 141 chars. (140 uft8, 1 unicode wide)

Complex function handling ~ or ¬, 254 chars (253 utf, 1 unicode wide), score 229

Could save 6 bytes using alert instead of console.log, but alert is particularly unfit to display tables.

Test running the snippet below in an EcmaScript 6 compliant browser (tested with Firefox. Won't work in Chrome as Chrome does not support .... Also, the bonus version use an extension of split that is Firefox specific).

/* TEST: redirect console.log into the snippet body */ console.log=x=>O.innerHTML+=x+'\n'

// Simple
F=s=>{[a,o,b]=[...s],z='  ',r=a+z+b+z+a+` ${o} ${b}
`;for(w='FT',n=4;n--;r+=w[c]+z+w[e]+z+z+w[o<'∧'?c|e:c&e]+`
`)c=n&1,e=n>>1;console.log(r)}

// Simple, more readable
f=s=>{
   [a,o,b]=[...s]
   r=a+'  '+b+'  '+a+` ${o} ${b}\n`
   for(w='FT',n=4; n--; )
   {
     c = n&1, e = n>>1, x=o<'∧' ? c|e : c&e
     r += w[c]+'  '+w[e]+'    '+w[x]+'\n'
   }
   console.log(r)
}

// 10% Bonus
B=s=>{[a,o,b]=s.split(/([∧∨])/),t=a>'z',u=b>'z',z='  ',r=(t?a[1]+z:'')+a+z+(u?b[1]+z:'')+b+z+a+` ${o} ${b}
`;for(s=v=>'FT'[v]+z,n=4;n--;r+=s(c)+(t?s(d)+' ':'')+s(e)+(u?s(f)+' ':'')+(t?'   ':z)+s(o<'∧'?d|f:d&f)+`
`)c=n&1,d=c^t,e=n>>1,f=e^u;console.log(r)}

Test1 = ['q∨p','q∧p']
Test2 = Test1.concat([
  '~q∨p','q∨~p','~q∨~p','~q∧p','q∧~p','~q∧~p',
  '¬q∨p','q∨¬p','¬q∨¬p','¬q∧p','q∧¬p','¬q∧¬p'
])


console.log('SIMPLE')
Test1.forEach(t=>F(t));

console.log('BONUS')
Test2.forEach(t=>B(t));
<pre id=O></pre>

edc65

Posted 2015-09-18T02:23:11.943

Reputation: 31 086

1+1, I love JavaScript and this solution deserves an upvote. – Arjun – 2015-09-18T10:44:31.177

JavaScript is my native language, but I won't let that affect me! :D Good Job! – MayorMonty – 2015-09-19T04:32:49.670

6

MediaWiki template - 2347 characters

MediaWiki has a built template function called {{#expr}} that can handle logical expressions. This must be the perfect challenge for MediaWiki templates! Features such as variables, loops and a readable syntax would have helped a bit, though. Also, the fact that there is no NOT operator for the expr function made it a bit more complex.

{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}} {{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}} {{{1}}}<br>T T &nbsp;{{#if:{{#pos:{{#sub:{{#replace:{{{1}}}|~|¬}}|0|1}}|¬}}|&nbsp;|}} {{#replace:{{#replace:{{#expr:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{{1}}}|~|¬}}|{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}}}|0|1}}}}|{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}}}|0|1}}|0|1}}|¬|}}|∧|and}}|∨|or}}}}|1|T}}|0|F}}<br>T F &nbsp;{{#if:{{#pos:{{#sub:{{#replace:{{{1}}}|~|¬}}|0|1}}|¬}}|&nbsp;|}} {{#replace:{{#replace:{{#expr:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{{1}}}|~|¬}}|{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}}}|0|1}}}}|{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}}}|1|0}}|1|0}}|¬|}}|∧|and}}|∨|or}}}}|1|T}}|0|F}}<br>F T &nbsp;{{#if:{{#pos:{{#sub:{{#replace:{{{1}}}|~|¬}}|0|1}}|¬}}|&nbsp;|}} {{#replace:{{#replace:{{#expr:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{{1}}}|~|¬}}|{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}}}|1|0}}}}|{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}}}|0|1}}|0|1}}|¬|}}|∧|and}}|∨|or}}}}|1|T}}|0|F}}<br>F F &nbsp;{{#if:{{#pos:{{#sub:{{#replace:{{{1}}}|~|¬}}|0|1}}|¬}}|&nbsp;|}} {{#replace:{{#replace:{{#expr:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{#replace:{{{1}}}|~|¬}}|{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{#replace:{{#replace:{{{1}}}|~|}}|¬|}}|0|1}}}}|1|0}}}}|{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}|{{#if:{{#pos:{{#replace:{{{1}}}|~|¬}}|¬{{#sub:{{{1}}}|{{#expr:{{#len:{{{1}}}}}-1}}|{{#len:{{{1}}}}}}}}}|1|0}}|1|0}}|¬|}}|∧|and}}|∨|or}}}}|1|T}}|0|F}}

Test:

{{TemplateName|¬X ∧ ~Y}}

{{TemplateName|p ∨ q}}

Result:

X Y ¬X ∧ ~Y
T T    F
T F    F
F T    F
F F    T

p q p ∨ q
T T   T
T F   T
F T   T
F F   F

I'm assuming MediaWiki >= 1.18, where the ParserFunctions extensions comes bundled with the software.

leo

Posted 2015-09-18T02:23:11.943

Reputation: 161

2Welcome to Programming Puzzles and Code Golf. Using MediaWiki is not something I would have thought of; +1. However, the extra column behaviour of the ¬ / ~ operator is missing; if you add it, you will qualify for a 10% bonus. – wizzwizz4 – 2016-05-30T12:48:42.253

I just realized that, unless I can use nested templates (that might be stretching the rules a bit too far?), properly adding that column would actually increase the character count... :) – leo – 2016-05-30T13:11:34.253

In which case, you should probably remove negation support, because you're not getting any bonuses for it. – wizzwizz4 – 2016-05-30T13:26:35.610

Yup, will look into it. I don't think it will have a lot of impact on the final ranking though... :D – leo – 2016-05-30T13:51:57.047

[tag:code-golf] is as much within languages as between them. – wizzwizz4 – 2016-05-30T14:03:22.293

1@leo This is great, and I think using nested templates would be fine if you just add the character count of the two of them, that seems to be the accepted practice nowadays. – Harry – 2017-01-11T12:35:21.890

4

Python - 288 characters (+10 penalty cause I couldn't get unicode to work :c)

No bonuses. This is my very first codegolf answer.

def f(i):
    i=i.split(" ")
    print i[0],i[2],
    for f in i[0:3]: print f,
    print ""
    for t in["TT","TF","FT","FF"]:
        p,q=t[0],t[1]
        y = t[0]+" "+t[1]
        if i[1]=="^": r=(False,True)[p==q]
        if i[1]=="v": r=(False,True)[p!=q]
        if r: y+="   T"
        else: y+="   F"
        print y

i is the input.

EDIT: Removed a few spaces and it now uses function args as input.

DJgamer98

Posted 2015-09-18T02:23:11.943

Reputation: 594

1Welcome to PP&CG! Please make sure to have your code following the rules, according to the question. As the rule specification, your code must be a function, full program or bit of code. This implies that input MUST be STDIN or function arguments (or equivalent) Happy Coding! – MayorMonty – 2015-09-22T23:40:49.770

3

Dyalog APL, 58 48 characters

Requires ⎕IO←0, which is default on many systems. Takes string as argument.

{('p q ',⍵)⍪'FT '[p,q,⍪⍎⍵]\⍨324⊤⍨9⍴≢p q←↓2 2⊤⌽⍳4}

No bonuses, but on the plus side, any operator works.

⍳4 first four indices (0 1 2 3)

 reverse (3 2 1 0)

2 2⊤ two-bit Boolean table

 split into two-element list of lists (high-bits, low-bits)

p q← store as p and q

 tally them (2)*

9⍴ cyclically reshape that to length 9 (2 2 2 2 2 2 2 2 2)

324⊤⍨ encode 324 thusly, i.e. as 12-bit binary (1 0 1 0 0 0 1 0 0)

\⍨ use that to expand (insert a space for each 0)...

'FT '[...] the string "FT ", indexed by

⍎⍵ the executed argument (valid since p and q now have values)

make that into a column matrix

q, prepend a column consisting of q (1 1 0 0)

q, prepend a column consisting of p (1 0 1 0)

(...)⍪ insert a row above, consisting of

 the argument

'p q ', prepended with the string "p q "


* Please star this issue if you see as ≢ and not as ̸≡.

Adám

Posted 2015-09-18T02:23:11.943

Reputation: 37 779

2

Julia, 161 bytes

No bonuses.

s->(S=split(s);P=println;p=S[1];q=S[3];a=[&,|][(S[2]=="∨")+1];c="  ";P(p,c,q,c,s);for t=["TT","TF","FT","FF"] P(t[1],c,t[2],c^2,"FT"[a(t[1]>'F',t[2]>'F')+1])end)

Ungolfed:

function f(s::String)
    # Split the input on spaces
    S = split(s)

    # Separate out the pieces of the statement
    p = S[1]
    q = S[3]
    a = [&, |][(S[2] == "∨") + 1]

    # Print the header
    println(p, "  ", q, "  ", s)

    # Create the table entries in a loop
    for t = ["TT", "TF", "FT", "FF"]
        println(t[1], "  ", t[2], "    ", "FT"[a(t[1] > 'F', t[2] > 'F') + 1])
    end
end

Alex A.

Posted 2015-09-18T02:23:11.943

Reputation: 23 761

1

C/C++ 302 Bytes

335 characters less 10% for handling negation. Formatting incomplete but submitting before I see what the impact of completion is.

Marked as C/C++ because my gcc and g++ accepts it with -fpermissive and it looks far more C like than C++ to me.

#include <stdio.h>
void T(char*S) { int (*P)(char*,...)=printf;char*v[2]={"F","T"};for(int m=4;m--;){P("|");char*s=S;int x=m&1;X:P(" %s |",v[x]);if(*++s!=' '){x=x^1;goto X;}char*o=++s;s+=3;int y=(m>>1)&1;Y:P(" %s |",v[y]);if(*++s){y=y^1;goto Y;}int g;for(g=o-S+1;g--;)P(" ");P(*++o==39?v[x&y]:v[x|y]);for(g=s-o;g--;)P(" ");P("|\n");}}

I'm sure there's probably a few tweaks that could be applied. In fact handling the nots adds more than the 10% bonus removes.

This does assume the input format is as stated, i.e. 2 input values (p and q), with or without the not prefix and nothing else, and all tokens delimited by a single space.

Ungolfed:

void ungolfed(char* S)
{
   int (*P)(char*,...) = printf;         // useful lookup stuff
   char* v[2] = {"F","T"};

   for(int m = 4; m--;) {                // loop over all 2 bit bit patterns (truth table inputs)

      P("|");                            // start of line format
      char* s=S;                         // iterator to start of equation for each bit pattern

      int x = m&1;                       // input 1 (aka. p which I called x here to be awkward)
X:    P(" %s |",v[x]);                   // input 1 output and format

      if(*++s!=' ') {                    // if next character is not a space then input must be prefixed with the not character
         x=x^1;                          // so negate the input
         goto X;                         // and redo input 1 output
      }

      char* o = ++s;                     // remember where the operator is
      s+=3;                              // and skip it and following space

      int y = (m>>1)&1;                  // input 2 (aka. q which I called y obviously) processing as for input 1
Y:    P(" %s |",v[y]);

      if(*++s) {
         y=y^1;
         goto Y;
      }

      int g;

      for(g=o-S+1;g--;) P(" ");         // pre-result value padding

      P(*++o==39?v[x&y]:v[x|y]);      // result

      for(g=s-o;g--;) P(" ");           // post-result value padding and format
      P("|\n");
   }
}

and the tests:

int main()
{
   T("p \x22\x27 q");  // p & q
   puts("");

   T("p \x22\x28 q");  // p | q
   puts("");

   T("\x7ep \x22\x27 q");  // ~p & q
   puts("");

   T("\xacp \x22\x28 q");  // ~p | q
   puts("");

   T("p \x22\x28 \xacq");  // p | ~q
   puts("");

   return 0;
}

original.legin

Posted 2015-09-18T02:23:11.943

Reputation: 31

1

Python3, 145 139 120 119 Bytes

No bonus (with bonus at the end)

 def f(s):
 a,m,b=s.split(" ");print(a,b,s);F,T,c=0,1,"FT"
 for p in c:
  for q in c:print(p,q," ",c[eval(p+"+*"[m=="∧"]+q)>0])

Needing Python3 for Unicode support out of the box.

Based on DJgamer98 Python code, figuring out his table is not right.

Edit1: Splitting into distinct variables and ommiting the operator string variable

Edit2: (ab)using F and T as both variables and string characters

Edit3: Saving one space thanks to NoOneIsHere

With Bonus, 215 * 0.6 = 129

def f(s):
 r="+---"*3+"----+"
 a,m,b=s.split(" ");F,T,c=0,1,"FT"
 print("%s\n| %s | %s | %s |\n%s"%(r,a,b,s,r));
 for p in c:
  for q in c: print("| %s | %s |   %s   |\n%s"%(p,q,c[eval(p+"+*"[m=="∧"]+q)>0],r));

Karl Napf

Posted 2015-09-18T02:23:11.943

Reputation: 4 131

Welcome to PPCG! You can save a byte by removing the space after q in c:. – NoOneIsHere – 2016-05-31T04:11:07.073

Edit2: That's not abuse. See here, where I use the first character of the file contents as filename!

– Adám – 2016-05-31T15:31:31.253

1

Mathematica, 129 Bytes

Golfed:

t=InputString[];s=Append[StringCases[t,LetterCharacter],t];Grid[Prepend[Map[If[#,"T","F"]&,BooleanTable[ToExpression[s]],{2}],s]]

Ungolfed:

(*Take input*)
t=InputString[];
(* Find all occurrences of letters and append the final statement.*)
s=Append[StringCases[t,LetterCharacter],t];
(* Evaluate the list as expressions and create a boolean table of True/False values, then display as a table. *)
(* To satisfy the output conditions, we must convert each True/False to T/F *)
Grid[Prepend[Map[If[#,"T","F"]&,BooleanTable[ToExpression[s]],{2}],s]]

Not a Mathematica expert, but I found this rather elegant compared to having to do direct character comparison.

I had a solution that worked for negation, but it was longer than the score reduction would take off.

Depending on what qualifies for pretty printing, I might try for that bonus. I feel like outputting in ASCII in Mathematica would be far too expensive for the score reduction to compensate, but if the two main features are a dotted border and specified padding inside the cells, that's only a couple options in Grid.

With pretty printing, 171 * 0.6 = 102.6 Bytes

t=InputString[];s=Append[StringCases[t,LetterCharacter],t];Grid[Prepend[Map[If[#,"T","F"]&,BooleanTable[ToExpression[s]],{2}],s],Spacings->1,Frame->All,FrameStyle->Dashed]

Xanderhall

Posted 2015-09-18T02:23:11.943

Reputation: 1 236

0

Mathematica, 128 characters

TraditionalForm@Grid[({#}~Join~BooleanTable[#,Cases[b,_Symbol,{0,∞}]]&/@Cases[b=ToExpression@#,_,{0,∞}]/.{0<1->"T",0>1->"F"})]&

is the private use character U+F3C7 representing \[Transpose].

Luckily for us Mathematica golfers, and already represent And and Or, so all we have to do is convert the input string into a Mathematica expression and we can do symbolic logical operations on it.

Note that this solution will also handle Not (¬), Implies (), Equivalent (), Xor (), Nand (), Xor (), and Nor (), but it doesn't get the bonus because ~p is a syntax error in Mathematica. Meh.

enter image description here

Explanation

b=ToExpression@#

Converts the input string into a Mathematica expression and stores it in b.

Cases[b=ToExpression@#,_,{0,∞}]

This is a list every possible subexpression of the input. Each will receive its own column.

Cases[b,_Symbol,{0,∞}]

This is a list of all of the variables that appear in the input.

BooleanTable[#,Cases[b,_Symbol,{0,∞}]]&

Pure function which takes an input expression # and returns a list of truth values for all possible combinations of truth values for the variables.

{#}~Join~BooleanTable[...]

Prepends the expression itself to this list.

.../@Cases[b=ToExpression@#,_,{0,∞}]

Applies this function to each subexpression of the input.

.../.{0<1->"T",0>1->"F"}

Then replace true (0<1) with "T" and false (0>1) with "F".

(...)

Interchange rows and columns.

Grid[...]

Display the result as a Grid.

TraditionalForm@Grid[...]

Convert the Grid to traditional form so that it uses the fancy symbols.

ngenisis

Posted 2015-09-18T02:23:11.943

Reputation: 4 600