Soroban Japanese Abacus
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()