Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Tallying is a simple counting system that works in base 5. There are various different tallying systems used around the world, but the one that is used in most English-speaking countries is perhaps the most simple - count units by marking vertical lines, then for each 5th mark put a horizontal line through the previous collection of four. This clusters the tally marks in groups of 5 (and makes them easier to quickly count).

You are going to write a program that displays tally marks up to a given value. But, tallying in only base 5 is boring! Therefore, your program should also be able to display tallies in different bases.

Input

The input will be either one or two non-negative integer values separated by a comma (e.g. 9 or 8,4). The first number is the value that should be displayed by the tally. The second value is the base of the tally. If the second value is not given, use base 5.

Output

The output will be the inputted value represented as ASCII art tally marks. Here are some examples you can test your program against - your output should match them exactly!

Input: 12 or 12,5

 | | | |   | | | |   | |
-+-+-+-+- -+-+-+-+-  | |
 | | | |   | | | |   | |

Input: 7,3

 | |   | |   |
-+-+- -+-+-  |
 | |   | |   |

Input: 4,2

 |   |
-+- -+-
 |   |

Input: 6,1 or 6,10 (notice the leading spaces)

 | | | | | |
 | | | | | |
 | | | | | |

Note also that base 1 is intended to be inconsistent - only vertical lines should be used.

If either of the inputted values is 0, there should be no output whatsoever (and your program should end gracefully).

Rules

  • This is , so the shortest correct implementation (in bytes) wins.
  • Input/output can be in any suitable medium (e.g. stdin/stdout, file...).
  • Input can be in the form of multiple command-line arguments or separated by spaces, etc. if it is more suitable for your target language.
  • Trailing newlines are allowed in the output. Trailing spaces are not. This rule only applies when there is an output (i.e. not when the inputted value is 0).
  • Your code must default to base 5 when no base is input.
share|improve this question
3  
Shouldn't the output of 6,1 look more like -+- -+- -+- -+- -+- -+-? –  Peter Taylor Aug 20 at 12:38
3  
If you state "The input will be either one or two positive integer values separated by a comma (e.g. 9 or 8,4)." then we should be able to take that as a given, and not have to worry about "Your program should be robust - you should validate the input..." beyond handling one or two numbers. –  AndoDaan Aug 20 at 12:42
1  
@PeterTaylor -+- would represent two, because there's a vertical line and a horizontal score through it. Base 1 would only have vertical lines. @AndoDaan ammended. –  ipi Aug 20 at 12:45
    
Ok, --- --- --- --- --- --- then. For consistency with the other bases you should be putting a horizontal strike through b-1 vertical lines. If it's intended to be inconsistent you should state that explicitly. –  Peter Taylor Aug 20 at 12:54
    
I've done that. Sorry, I thought it was implied. –  ipi Aug 20 at 13:03

13 Answers 13

up vote 4 down vote accepted

CJam 103 85 72 characters

Try it at http://cjam.aditsu.net/.

original

q","/(i:A\_,{~i}{;5}?:B_@*{(_{" |"*S"l"++AB/*AB%}{;MA}?\:J" |l""-+ "er\" |"*N+_J\+@2$+@J\+++"l"Ser}{;}?

Works by defining one set of tallys with spaces, lines and an l for spaces that should remain spaces. Then takes advantage of er (tranliteration) function to make the second line. Most inefficient part is dealing with the 1 and 0 special cases. Will edit as I improve it. Tip I took too long to realize: as the second input being 1 is the same as infinity or the first input +1, redefining it when it is equal to 1 saves alot of work.

most improved so far with comma delimited

l",":G/(i:A\_5a?~i:B_@*{(_" |":K*\{SG++AB/*AB%}{A}?\_KG+"-+ "er[\GSer_@@M]\K*N+*}{;}?

most improved so far with space delimited input

Naturally as CJam is really designed for space delimited input. Placing the input at 20 3 instead of 20,3 is a huge benefit.

ri:Aq_5s?S-i(_)A)?:B*{B(" |":K*STs++ABmd@@*_K"-+"er[\_@@M]\K*N+*TsSer}M?
share|improve this answer
    
It's been a week, so I'll accept this as the shortest answer. Well done! –  ipi Aug 27 at 11:05

Python 2 - 111 108 119 144 140 136 135 134 - Try it

Ok, let's give it a try:

i=input()
n,b=[(i,5),i][i>[]]
o=b-1
a=[n,n%b][b>1]*' |'
m=(b>1)*n/b
s=(' |'*o+'  ')*m+a
print(s+'\n'+('-+'*o+'- ')*m+a+'\n'+s)*(b*n>0)

Edit: I've overlooked that there should be no output if n==0 or b==0. This costs me 11 characters. :(

Edit: Ok, after fixing the second issue mentioned in the comments my solution basically converged to the one from BeetDemGuise.

share|improve this answer
    
This prints newlines when either of the inputs (or both) are zero which according the challenge is undesired. Also, what if only one number is input into the program? –  BeetDemGuise Aug 20 at 13:40
1  
This fails when the second value is omitted (b should be 5 in this case). I'll make it more clear in the question. Edit: oh, never mind, you fixed it just as I made this comment! –  ipi Aug 20 at 13:44
    
Which Python is this? –  Beta Decay Aug 20 at 14:46
    
It's Python 2.7.8. - Oh, there was a tiny mistake at the very end... –  Falko Aug 20 at 14:48
1  
If it's Python 2.x couldn't you save one more character by using n/binstead of n//b? –  Emil Aug 20 at 15:31

Bash, 239 228 199 189 188

Here's my attempt, it could be golfed a lot.

Note: the second line does not subtract 5 from 2, it sets a default value if $2 is empty!

n=$1
b=${2-5}
((n<1&b<1))&&exit
while ((n>b&b>1));do
n=$[n-b]
x+=\
y+=-
for i in `seq $[b-1]`;{
x+='| '
y+=+-
}
x+=\
y+=\
done
for j in `seq $n`;{
x+=' |'
y+=' |'
}
echo -e "$x\n$y\n$x"
share|improve this answer
    
Does {1..$n} work instead of `seq $n`? –  FUZxxl Aug 21 at 14:27
    
@FUZxxl unfortunately not, h=8;echo {1..$h} outputs {1..8} –  professorfish Aug 21 at 16:58
    
That's not good. –  FUZxxl Aug 21 at 17:08

C - 193

I'm so sorry. Dealing with the special case for 1 was a bit of a bad hack so I guess this could be golfed more with a better approach. Also, this code includes a new line at the beginning of the output, so if this is not allowed, please let me know.

Of course, very ugly-looking defines always help :)

Character count includes only necessary spaces and new lines.

#define P printf(
#define L P" |")
#define A P"\n");for(i=0;i<a;)b==1?i++,L:i++&&i%b==0?P
i;
main(a,b)
{
    scanf("%d,%d",&a,&b)<2?b=5:!b?a=0:a;
    if(a){
    A"  "):L;
    A"- "):a%b&&i>a/b*b?L:P"-+");
    A"  "):L;}
}
share|improve this answer
    
Your code seems to print newlines when one of the values is zero. This is explictly disallowed. Your solution is not conforming. –  FUZxxl Aug 21 at 15:54
    
@FUZxxl You're right, I missed that! This bad quick fix will have to do for now. I hope I have time soon to find a better way. –  Allbeert Aug 21 at 16:17
    
I'm pretty sure you can save a few characters by replacing printf with puts, and replacing return with a ternary operator. –  millinon Aug 23 at 23:51
    
@millinon The problem with puts is that it adds a new line every time :(. And for the ternary operator, it is not possible to add returns or fors inside them!. Your comment did give me the idea to save a few more characters very easily by removing the return though. Thanks! –  Allbeert Aug 26 at 20:38

Python - 171 143

i=input();t,b=i if[0]<i else(i,5);b=[b,t+1][b==1];l,d,m,o=' |',t/b,t%b,b-1;r=(l*o+'  ')*d+l*m
if t*b>0:print r,'\n',('-+'*o+'- ')*d+l*m,'\n',r

The program is pretty straight-forward:

  • Get the input and try to unpack into t,b. If that fails, simply assign the correct values.
  • If the base was 1, change its value to something that can handle all vertical lines easily (t+1).
  • Set some variables and create the bottom and top sections of the tallies.
  • Print out the tallies if both t and b are non-zero.

EDIT 1: Use the input function instead of raw_input after some playing around.

EDIT 2: Thanks to Falko for pointing out a small bug with my non-zero checking. Now our code is basically identical, less some variable names and some small logic.

EDIT 3: Thanks to how Python compares sequences and different types, we can compare i to a list to get a shorter version of our try...except block.

Here is the ungolfed version:

i=input()

# If True, `i` must be a list
if [0]<i:
    t,b=i
# Otherwise, we know its a number (since `list` comes after `int` lexicographically.)
else:
    b=5
    t=i

b = b if b==1 else t+1
l=' |'
d=t/b
m=t%b
o=b-1

r=(l*o+'  ')*d+l*m
if t and b:
    print r,'\n',('-+'*o+'- ')*d+l*m,'\n',r
share|improve this answer
    
I think t&b is False for 10,5. Otherwise our solutions are converging! ;) –  Falko Aug 20 at 13:51
    
@Falko You are right on both counts! You know what they say about great minds. –  BeetDemGuise Aug 20 at 13:56
    
It'd be really great if we could find a short way to test if i is scalar or a list. Then we could drop the try ... except monster. –  Falko Aug 20 at 14:09
    
@Falko I think I found a 1byte-better check. A list is always greater than an int. Also, lists are compared in lexicographical order. So if we compare [0]<i this will always return False if i is a number and True if i is a list (with a non-zero first element). –  BeetDemGuise Aug 20 at 14:24
1  
Great! I further shortened your approach. Nice teamwork! :) –  Falko Aug 20 at 14:32

Java, 343

class C{public static void main(String[]a){long n=new Long(a[0])+1,b=a.length>1?new Long(a[1]):5;if(b>0){if(b<2)b=(int)2e9;int i;for(i=1;i<n;i++)p(i%b>0?" |":"  ");p("\n");for(i=1;i<n-n%b;i++)p(i%b>0?"-+":"- ");if(n>b)p("- ");for(i=1;i<n%b;i++)p(" |");p("\n");for(i=1;i<n;i++)p(i%b>0?" |":"  ");}}static void p(String s){System.out.print(s);}}

Less golfed:

class C {
  public static void main(String[] a) {
    long n=new Long(a[0])+1, b=a.length>1 ? new Long(a[1]) : 5;
    if(b>0) {
      if(b<2) b=(int)2e9; // if the base is 1, pretend the base is 2 billion
      int i;
      for(i=1;i<n;i++) p(i%b>0 ? " |" : "  ");
      p("\n");
      for(i=1;i<n-n%b;i++) p(i%b>0 ? "-+" : "- ");
      if(n>b) p("- ");
      for(i=1;i<n%b;i++) p(" |");
      p("\n");
      for(i=1;i<n;i++) p(i%b>0 ? " |" : "  ");
    }
  }
  static void p(String s) {
    System.out.print(s);
  }
}
share|improve this answer
    
You can save a few by making i a long so you don't have to declare it separately. A few more by doing i++%b>0 in your loops instead of incrementing it separately (and i++<n%b in the third loop). Another one by using b=b<2?(int)2e9:b. –  Geobits Aug 21 at 13:29

Perl - 167 165 156

my($n,$b)=($ARGV[0]=~/(\d+)(?:,(\d+))?/,5);print$b>1?map{join(" ",($_ x($b-1))x int($1/$b)," | "x($1%$b))."\n"}(" | ","-+-"," | "):join" ",("---")x$1if$1*$b

ungolfed

my($n,$b) = ($ARGV[0] =~ /(\d+)(?:,(\d+))?/, 5);
print($b>1 ?
    map{ 
        join(" ",($_ x ($b-1)) x int($1/$b)," | " x ($1%$b))."\n"
    } (" | ","-+-"," | ") :
    join " ", ("---") x $1
) if $1 * $b
share|improve this answer
    
displays horizontal lines instead of vertical ones for base 1 :( –  chinese perl goth Aug 21 at 15:13
    
@chineseperlgoth yes, that's one of the requirements. Please read comments on Q. –  Fozi Aug 23 at 1:35

C# 271bytes

Not the shortest, I couldn't golf the input reading due to it needing to accept 0 as an input.

using C=System.Console;class P{static void Main(){var L=C.ReadLine().Split(',');int t=int.Parse(L[0]),f=L.Length>1?int.Parse(L[1]):5,r=3,i;for(var R="";t*f>0&r-->0;C.WriteLine(R.TrimEnd()))for(R="",i=0;i<t;R+=r==1&i++<t-t%f?(i%f<1?"- ":"-|"):i%f<1?"  ":" |")f+=f<2?t:0;}}

Formatted code:

using C=System.Console;

class P
{
    static void Main()
    {
        var L=C.ReadLine().Split(',');
        int t=int.Parse(L[0]),f=L.Length>1?int.Parse(L[1]):5,r=3,i;

        for(var R="";t*f>0&r-->0;C.WriteLine(R.TrimEnd()))
            for(R="",i=0;i<t;R+=r==1&i++<t-t%f?(i%f<1?"- ":"-|"):i%f<1?"  ":" |")
                f+=f<2?t:0;
    }
}
share|improve this answer

Lua - 219 203 bytes

I went for the make d copies of b copies of "|", then add r copies of "|" at the end. I feel like maybe I should have gone with the 'tally up' the "|"s to the string one at a time.

l=' |'s=string.rep _,_,a,b=io.read():find'(%d+)%D*(%d*)'b=tonumber(b)or 5 d=(a-a%b)/b f=b>1 and s(s(l,b-1)..'  ',d)g=b>1 and s(s('-+',b-1)..'- ',d)r=b>1 and a%b or a e=s(l,r)..'\n'print(f..e..g..e..f..e)

ungolfed:

l=' |'          --the base string
s=string.rep    --string.rep will be used a lot, so best shorten it

_,_,a,b=io.read():find'(%d+)%D*(%d*)' --reads a,b I'm probably way of the mark with this one

b=tonumber(b)or 5

d=(a-a%b)/b -- shorter than math.floor(a/b), d equal the vertical mark

f=b>1 and s(s(l,b-1)..'  ',d) or '' --creates d multiples of b multiples of "|" more or less
g=b>1 and s(s('-+',b-1)..'- ',d)or''--same as above but with the middle "-+-"

r=b>1 and a%b or a --idk maybe i should set r before d(a- a%b )/b

e=s(l,r)..'\n'  -- makes the remainder string, notice that if b==1  then e will output all the "|" 

print(f..e..g..e..f..e) -- here's where the real magic happens!

Sample:

c:\Programming\AnarchyGolfMine>lua test.lua
13,5
 | | | |   | | | |   | | |
-+-+-+-+- -+-+-+-+-  | | |
 | | | |   | | | |   | | |


c:\Programming\AnarchyGolfMine>lua test.lua
6,2
 |   |   |
-+- -+- -+-
 |   |   |


c:\Programming\AnarchyGolfMine>lua test.lua
18,1
 | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | |
share|improve this answer
1  
Could you post a sligthly more readable and ungolfed version? Golfing in Lua looks interesting! –  Alessandro Aug 20 at 16:03
    
@Alessandro done. And thanks, it made find a couple things I missed. –  AndoDaan Aug 20 at 17:43

JavaScript (193)

This might be overly complex.

s=prompt().split(",");a=+s[0];b=s[1];b=b?+b:5;o=b>1;v=" | ";q=w="";for(g=~~(a/b),c=o?a-g:a;g+1;g--,q+=" ",w+=" ")for(i=o;c&&i<b;i++)c--,q+=v,w+=g&&o?"-+-":v;if(a&&b)console.log(q+'\n'+w+'\n'+q)

Commented version

s=prompt().split(",");
a=+s[0];
b=s[1];
b=b?+b:5;   // convert b to int and default to 5
o=b>1;      // special handling for b0 and b1
v=" | ";
q=w="";
// calculate number of lines and number of groups
for(g=~~(a/b),c=o?a-g:a;g+1;g--,q+=" ",w+=" ")
    for(i=o;c&&i<b;i++)
        c--,  // decrease line count
        q+=v,
        w+=g&&o?"-+-":v; // use " | " for last group and "-+-" for others
if(a&&b) // handle b0
    console.log(q+'\n'+w+'\n'+q)
share|improve this answer

Python - 127 123 122

Just sneaking in with a slightly shorter python version.

edit: Fixed 0 not printing nothing and rejigger, ended up the same length

k=input()
k=i,j=((k,5),k)[k>[]]
for m in[' |','-+',' |']*all(k):
 print(m*(j-1)+m[0]+' ')*(i/j*(j>1))+' |'*(i%(j+(j<2)*i))
share|improve this answer

C (207 characters)

The newline right before exit is for legibility only.

#define P printf(
#define t(v)for(a=c;b<=a;a-=b)a-c&&P" "),P v+1),q(b,v);q(a," |");P"\n");
q(n,c){while(n--)P c);}a;b;c;main(){scanf("%d,%d",&c,&b)<2?b=5:0;b&&c||
exit();b==1?b=32767:0;t("| ")t("+-")t("| ")}

scanf usage shamelessy stolen from Allbeert. Notice that this solution does not compile with gcc as it tries to enforce a nonexistant prototype for exit. Compile with a working C compiler like tcc. Also, this function may or may not work on 64 bit platforms. Use with care.

Here is the original ungolfed implementation this is based on:

#include <stdio.h>
#include <stdlib.h>

static void
tally_line(int base, int count, const char *str)
{
     int follower = 0, i;

     /* full tallies first */
     for (; count >= base; count -= base) {
          if (follower++)
               putchar(' ');

          /* only print second character */
          printf(str + 1);

          for (i = 0; i < base; i++)
               printf(str);
     }

     /* partial tally */
     for (i = 0; i < count; i++)
          printf(" |");

     /* newline */
     puts("");
}

extern int
main(int argc, char **argv)
{
     int base, count;

     /* do away with program name */
     count = atoi(*++argv);

     base = argc - 3 ? 5 : atoi(*++argv);

     /* remove 0 later */
     base | count || exit(0);

     /* a crossed-out tally never appears for large numbers */
     if (base == 1)
          base = 32767;

     tally_line(base, count, "| ");
     tally_line(base, count, "+-");
     tally_line(base, count, "| ");

     return (EXIT_SUCCESS);
}
share|improve this answer

Ruby - 130

n,b=*($*+[5]).map(&:to_i);(f=n/b;m=n%b;m,f=n,0 if b<2;puts [0,p,0].map{|i|((i ?' |':'-+')*(b-1)+(i ?'  ':'- '))*f+' |'*m})if b*n>0

(Args should be separated by spaces)
I think I can golf it more !

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.