Soroban Japanese Abacus

Tags: large, artistic, math, simulation

An abacus, also called a counting frame, is a calculating tool used in many cultures long before electronic calculators were invented. Figure 70-1 shows the Japanese form of the abacus, called a soroban. Each wire represents a place in a positional numeral system, and the beads on the wire represent the digit at that place. For example, a soroban with two beads moved over on the rightmost wire and three beads moved over on the second-to-rightmost wire would represent the number 32. This program simulates a soroban. (The irony of using a computer to simulate a pre-computer computing tool is not lost on me.)

Each column in the soroban represents a different digit. The rightmost column is the ones place, the column to its left is the tens place, the column to the left of that is the hundreds place, and so on. The Q, W, E, R, T, Y, U, I, O, and P keys along the top of your keyboard can increase the digit at their respective positions, while the A, S, D, F, G, H, J, K, L, and ; keys will decrease them. The beads on the virtual soroban will slide to reflect the current number. You can also enter numbers directly.

The four beads below the horizontal divider are “earth” beads, and lifting them up against the divider counts as 1 for that digit. The bead above the horizontal divider is a “heaven” bead, and pulling it down against the divider counts as 5 for that digit, so pulling down one heaven bead and pulling up three earth beads in the tens column represents the number 80. More information about abacuses and how to use them can be found at https://en.wikipedia.org/wiki/Abacus.

soroban_japanese_abacus.py
  1"""Soroban Japanese Abacus, by Al Sweigart al@inventwithpython.com
  2A simulation of a Japanese abacus calculator tool.
  3More info at: https://en.wikipedia.org/wiki/Soroban
  4This code is available at https://nostarch.com/big-book-small-python-programming
  5Tags: large, artistic, math, simulation"""
  6
  7NUMBER_OF_DIGITS = 10
  8
  9
 10def main():
 11    print('Soroban - The Japanese Abacus')
 12    print('By Al Sweigart al@inventwithpython.com')
 13    print()
 14
 15    abacusNumber = 0  # This is the number represented on the abacus.
 16
 17    while True:  # Main program loop.
 18        displayAbacus(abacusNumber)
 19        displayControls()
 20
 21        commands = input('> ')
 22        if commands == 'quit':
 23            # Quit the program:
 24            break
 25        elif commands.isdecimal():
 26            # Set the abacus number:
 27            abacusNumber = int(commands)
 28        else:
 29            # Handle increment/decrement commands:
 30            for letter in commands:
 31                if letter == 'q':
 32                    abacusNumber += 1000000000
 33                elif letter == 'a':
 34                    abacusNumber -= 1000000000
 35                elif letter == 'w':
 36                    abacusNumber += 100000000
 37                elif letter == 's':
 38                    abacusNumber -= 100000000
 39                elif letter == 'e':
 40                    abacusNumber += 10000000
 41                elif letter == 'd':
 42                    abacusNumber -= 10000000
 43                elif letter == 'r':
 44                    abacusNumber += 1000000
 45                elif letter == 'f':
 46                    abacusNumber -= 1000000
 47                elif letter == 't':
 48                    abacusNumber += 100000
 49                elif letter == 'g':
 50                    abacusNumber -= 100000
 51                elif letter == 'y':
 52                    abacusNumber += 10000
 53                elif letter == 'h':
 54                    abacusNumber -= 10000
 55                elif letter == 'u':
 56                    abacusNumber += 1000
 57                elif letter == 'j':
 58                    abacusNumber -= 1000
 59                elif letter == 'i':
 60                    abacusNumber += 100
 61                elif letter == 'k':
 62                    abacusNumber -= 100
 63                elif letter == 'o':
 64                    abacusNumber += 10
 65                elif letter == 'l':
 66                    abacusNumber -= 10
 67                elif letter == 'p':
 68                    abacusNumber += 1
 69                elif letter == ';':
 70                    abacusNumber -= 1
 71
 72        # The abacus can't show negative numbers:
 73        if abacusNumber < 0:
 74            abacusNumber = 0  # Change any negative numbers to 0.
 75        # The abacus can't show numbers larger than 9999999999:
 76        if abacusNumber > 9999999999:
 77            abacusNumber = 9999999999
 78
 79
 80def displayAbacus(number):
 81    numberList = list(str(number).zfill(NUMBER_OF_DIGITS))
 82
 83    hasBead = []  # Contains a True or False for each bead position.
 84
 85    # Top heaven row has a bead for digits 0, 1, 2, 3, and 4.
 86    for i in range(NUMBER_OF_DIGITS):
 87        hasBead.append(numberList[i] in '01234')
 88
 89    # Bottom heaven row has a bead for digits 5, 6, 7, 8, and 9.
 90    for i in range(NUMBER_OF_DIGITS):
 91        hasBead.append(numberList[i] in '56789')
 92
 93    # 1st (topmost) earth row has a bead for all digits except 0.
 94    for i in range(NUMBER_OF_DIGITS):
 95        hasBead.append(numberList[i] in '12346789')
 96
 97    # 2nd earth row has a bead for digits 2, 3, 4, 7, 8, and 9.
 98    for i in range(NUMBER_OF_DIGITS):
 99        hasBead.append(numberList[i] in '234789')
100
101    # 3rd earth row has a bead for digits 0, 3, 4, 5, 8, and 9.
102    for i in range(NUMBER_OF_DIGITS):
103        hasBead.append(numberList[i] in '034589')
104
105    # 4th earth row has a bead for digits 0, 1, 2, 4, 5, 6, and 9.
106    for i in range(NUMBER_OF_DIGITS):
107        hasBead.append(numberList[i] in '014569')
108
109    # 5th earth row has a bead for digits 0, 1, 2, 5, 6, and 7.
110    for i in range(NUMBER_OF_DIGITS):
111        hasBead.append(numberList[i] in '012567')
112
113    # 6th earth row has a bead for digits 0, 1, 2, 3, 5, 6, 7, and 8.
114    for i in range(NUMBER_OF_DIGITS):
115        hasBead.append(numberList[i] in '01235678')
116
117    # Convert these True or False values into O or | characters.
118    abacusChar = []
119    for i, beadPresent in enumerate(hasBead):
120        if beadPresent:
121            abacusChar.append('O')
122        else:
123            abacusChar.append('|')
124
125    # Draw the abacus with the O/| characters.
126    chars = abacusChar + numberList
127    print("""
128+================================+
129I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
130I  |  |  |  |  |  |  |  |  |  |  I
131I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
132+================================+
133I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
134I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
135I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
136I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
137I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
138I  {}  {}  {}  {}  {}  {}  {}  {}  {}  {}  I
139+=={}=={}=={}=={}=={}=={}=={}=={}=={}=={}==+""".format(*chars))
140
141
142def displayControls():
143    print('  +q  w  e  r  t  y  u  i  o  p')
144    print('  -a  s  d  f  g  h  j  k  l  ;')
145    print('(Enter a number, "quit", or a stream of up/down letters.)')
146
147
148if __name__ == '__main__':
149    main()

https://inventwithpython.com/bigbookpython/project70.html