Base 6 Number System in Python - In Project Hail Mary, the alien engineer Rocky counts in base 6 because he has two hands with three fingers each. In this article, you'll build Python programs that convert between decimal, base-6, and Eridian notation.

Base 6 Number System in Python

If you've read Andy Weir's Project Hail Mary, you probably remember (spoiler alert!) the moment when Ryland Grace tries to communicate with Rocky, the alien engineer from the 40 Eridani star system. They don't share a language, number of limbs, or even a body temperature. But they do share one thing: mathematics. It's the universal language, and it becomes their bridge.

During those early communication attempts, Grace and Rocky use clocks, capsules and basic math to figure out each other's chemistry fo atmosphere and number systems. Grace watches Rocky's clock - a cylinder with five squares of changing symbols - and quickly notices something: Eridians don't count in base 10 like we do. They use base 6, a senary number system.

Why base 6? Because of anatomy. Eridians are pentapods - they have five legs radiating from a pentagonal thorax, like a rocky spider. Their normal posture uses three legs for standing (the minimum needed for stability), leaving two free limbs to use as hands. Each of those hands has three fingers. Two hands times three fingers gives six digits for counting. It's the same reason we use base 10 - we have ten fingers. Evolution just dealt the Eridians a different hand. Literally.

It's also worth noting that one fan theory points out an interesting mathematical property: while Eridians could theoretically use base 15 (five hands, three fingers each), base 6 is a superior highly composite number, making it more practical for everyday division. Fractions like 1/2 and 1/3 come out clean in base 6 (0.3 and 0.2 respectively), while in our decimal system, 1/3 gives us the repeating 0.333... Rocky would probably find our number system frustratingly imprecise.

A note on experience: I first ran into non-decimal number systems when working on old software converting RGB colors to hexadecimal values for web usage. Base conversion isn't just science fiction - it's a real skill used in aerospace software, embedded systems and network protocols. Weir's book just happens to make it a lot more fun to learn.

What Is a Base-6 (Senary) Number System?

Before we write any Python, let's make sure we understand what "base" actually means in the context of number systems.

In our everyday decimal system (base 10), we have ten symbols: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. When we run out of single digits at 9, we move to the next position and write 10 - meaning "one group of ten, plus zero ones."

In base 6, we only have six symbols: 0, 1, 2, 3, 4, 5. When we hit 5, we've used up all our available digits. The next number isn't 6 - it's 10 (in base 6), which means "one group of six, plus zero ones."

Here's a comparison table to make this click. Let's count from 0 to 12 in both systems:

Decimal (Base 10) Senary (Base 6) What's happening
0 0 Same in both systems
1 1  
2 2  
3 3  
4 4  
5 5 Last single digit in base 6
6 10 Out of digits - new position!
7 11  
8 12  
9 13  
10 14  
11 15 Out of digits again in the ones place
12 20 Two groups of six, zero ones

See the pattern? It works exactly like decimal counting, except you "roll over" to the next position after every 6 values instead of every 10.

How Position Values Work in Base 6

In base 10, each position represents a power of 10. The rightmost digit is the ones place (100 = 1), then the tens place (101 = 10), then the hundreds place (102 = 100), and so on.

In base 6, each position represents a power of 6:

# Position values in base 6
# Position:    2       1       0
# Value:      6**2    6**1    6**0
#              36       6       1

# So the senary number 245 means:
# 2 * 36 + 4 * 6 + 5 * 1 = 72 + 24 + 5 = 101 in decimal

Once you see this, conversion becomes a simple mechanical process. Let's teach Python how to do it.

Converting Decimal to Base 6 - Your Own Function

The algorithm is straightforward: keep dividing the decimal number by 6 and collect the remainders. Those remainders, read in reverse order, give you the base-6 number.

def decimal_to_base6(decimal_number):
    if decimal_number == 0:
        return "0"
    
    digits = []  # We'll collect remainders here
    number = decimal_number
    
    while number > 0:
        remainder = number % 6   # Get the last base-6 digit
        digits.append(str(remainder))
        number = number // 6     # Remove that digit
    
    # Remainders come out in reverse order, so we flip them
    digits.reverse()
    return "".join(digits)

# Let's test it
print(decimal_to_base6(12))   # Output: 20
print(decimal_to_base6(36))   # Output: 100
print(decimal_to_base6(101))  # Output: 245
print(decimal_to_base6(255))  # Output: 1103

Walk through the logic with the number 101: first division gives 101 / 6 = 16 remainder 5. Then 16 / 6 = 2 remainder 4. Then 2 / 6 = 0 remainder 2. Read the remainders backwards: 245. That's 101 in base 6.

Converting Decimal to Base 6 - The Quick Way

Python doesn't have a built-in function for base 6 specifically, but numpy has a handy tool called base_repr() that converts an integer to any base from 2 to 36:

import numpy as np

# numpy.base_repr(number, base) returns a string
print(np.base_repr(12, 6))    # Output: 20
print(np.base_repr(36, 6))    # Output: 100
print(np.base_repr(101, 6))   # Output: 245
print(np.base_repr(255, 6))   # Output: 1103

One line of code, same result. For production code or quick scripts, this is the way to go. But understanding the manual method is what separates someone who copies code from someone who actually gets what's going on under the hood.

Converting Base 6 to Decimal - Your Own Function

Going the other direction: take each digit, multiply it by its position value (a power of 6), and add everything up.

def base6_to_decimal(base6_string):
    decimal_value = 0
    
    for digit in base6_string:
        # Shift existing value left (multiply by 6) and add new digit
        decimal_value = decimal_value * 6 + int(digit)
    
    return decimal_value

# Let's verify our earlier conversions
print(base6_to_decimal("20"))    # Output: 12
print(base6_to_decimal("100"))   # Output: 36
print(base6_to_decimal("245"))   # Output: 101
print(base6_to_decimal("1103"))  # Output: 255

The trick here is elegant: instead of calculating powers of 6 separately, we just multiply the running total by 6 each time we process a new digit. This is known as Horner's method, and it's used in real-world parsers and compilers. For example, "245" processes like this: start with 0, then (0 x 6) + 2 = 2, then (2 x 6) + 4 = 16, then (16 x 6) + 5 = 101.

Converting Base 6 to Decimal - The Quick Way

Python's built-in int() function can parse a string in any base from 2 to 36. Just pass the base as the second argument:

# int(string, base) converts from any base to decimal
print(int("20", 6))     # Output: 12
print(int("100", 6))    # Output: 36
print(int("245", 6))    # Output: 101
print(int("1103", 6))   # Output: 255

No imports needed. This is a built-in Python feature that many beginners don't know about. It works for binary (int("1010", 2)), octal (int("17", 8)), hexadecimal (int("FF", 16)), and yes - base 6.

Displaying Numbers in Eridian Notation

Now for the fun part. In Project Hail Mary, Rocky's species uses six unique symbols for their digits instead of our familiar 0 through 5. Based on the book, the Eridian numeral symbols are:

Decimal digit Eridian symbol Description
0 Cursive L
1 Capital I with serifs
2 V Letter V
3 λ Lambda
4 + Plus sign
5 Turned A (upside-down A)

Note: The exact rendering of Eridian numerals varies between editions and formats of the book. Weir designed them as simple geometric shapes that a blind species using echolocation could distinguish by touch. The symbols above are the most commonly used representations in fan communities and the official wiki.

Let's build a converter that takes a decimal number and displays it in Eridian style. Since we already know numpy.base_repr() handles the base-6 conversion, we can keep the code short:

import numpy as np

# Eridian digit symbols (0 through 5)
ERIDIAN_DIGITS = {
    "0": "ℓ",
    "1": "Ɪ",
    "2": "V",
    "3": "λ",
    "4": "+",
    "5": "∀"
}

# Reverse mapping for converting Eridian back to digits
ERIDIAN_TO_DIGIT = {v: k for k, v in ERIDIAN_DIGITS.items()}

def to_eridian(decimal_number):
    """Convert a decimal number to Eridian (base-6) notation."""
    if decimal_number == 0:
        return "ℓ"
    
    base6 = np.base_repr(decimal_number, 6)
    return "".join(ERIDIAN_DIGITS[d] for d in base6)

def from_eridian(eridian_string):
    """Convert an Eridian number back to decimal."""
    base6_string = "".join(ERIDIAN_TO_DIGIT[ch] for ch in eridian_string)
    return int(base6_string, 6)

# Examples from space exploration
print(f"Days in a year: 365 = {to_eridian(365)} in Eridian")
print(f"ISS speed (km/h): 27600 = {to_eridian(27600)} in Eridian")
print(f"Earth years in a century: 100 = {to_eridian(100)} in Eridian")

# And convert back
print(f"Eridian V+∀ = {from_eridian('V+∀')} in decimal")

Notice how we create the reverse mapping (ERIDIAN_TO_DIGIT) using a dictionary comprehension instead of typing it out manually. Less typing, fewer bugs.

Putting It All Together: A Mini Converter Tool

Here's a small but complete program that combines everything we've built. Think of it as a communication tool for your first contact scenario. No fancy functions - just straightforward code you can run right away:

import numpy as np

# Eridian digit symbols
ERIDIAN_DIGITS = {
    "0": "ℓ", "1": "Ɪ", "2": "V",
    "3": "λ", "4": "+", "5": "∀"
}

print("=== Earth-Eridian Number Converter ===")
print("1. Decimal to Base 6")
print("2. Base 6 to Decimal")
print("3. Decimal to Eridian symbols")

choice = input("Pick an option (1/2/3): ")

if choice == "1":
    num = int(input("Enter a decimal number: "))
    result = np.base_repr(num, 6)
    print(f"{num} in base 6 is: {result}")

elif choice == "2":
    base6 = input("Enter a base-6 number (digits 0-5 only): ")
    if any(d not in "012345" for d in base6):
        print("Error: base-6 numbers can only contain digits 0-5.")
    else:
        result = int(base6, 6)
        print(f"{base6} (base 6) = {result} in decimal")

elif choice == "3":
    num = int(input("Enter a decimal number: "))
    base6 = np.base_repr(num, 6)
    eridian = "".join(ERIDIAN_DIGITS[d] for d in base6)
    print(f"{num} = {base6} (base 6) = {eridian} (Eridian)")

else:
    print("Invalid choice. Please enter 1, 2, or 3.")

Why Base 6? Beyond the Fiction

Andy Weir didn't pick base 6 randomly. The number system a species uses is tied to their anatomy - specifically how many appendages they use for counting. Humans settled on base 10 because of our ten fingers. The ancient Babylonians used base 60 (a multiple of 6, interestingly enough), and that's why we still have 60 seconds in a minute and 360 degrees in a circle.

In fact, some mathematicians argue that base 6 or base 12 would be more practical than base 10 for everyday use. The number 6 has factors of 1, 2, and 3, which means dividing things into halves and thirds is clean and easy. Our base 10 only factors neatly by 2 and 5, which is why splitting a pizza among three people always results in that awkward repeating decimal.

In real-world computing, base conversion is everywhere: binary (base 2) for machine code, octal (base 8) for Unix file permissions, hexadecimal (base 16) for colors and memory addresses and in The Martian movie for ASCI based, camera turn communication with Earth. Understanding how bases work isn't just a fun exercise - it's foundational computer science knowledge that shows up the moment you start working with low-level code, networking, or data encoding.

Training Missions

Now it's your turn. Try these exercises to strengthen your base conversion skills:

Mission 1: Eridian Multiplication Table

Create a program that prints a multiplication table from 1 to 5 (Ɪ to ∀ in Eridian) using Eridian notation. The output should look like a grid where row headers, column headers, and results are all in Eridian symbols. Hint: do the multiplication in decimal, then convert the result to Eridian for display.

Mission 2: Eridian Clock

In the book, the Eridian day is 18,397 Earth seconds long, divided into 10,000 base-6 units (that's 7,776 in decimal). Each Eridian "second" equals about 2.366 Earth seconds. Write a program that takes a number of Earth seconds as input and converts it to Eridian time displayed in Eridian numerals. For example: 3,600 Earth seconds (one Earth hour) should show how many Eridian time units that is, written in Eridian symbols.

Mission 3: Universal Base Converter

Write a program that asks the user for a number, a source base (2-16), and a target base (2-16), then performs the conversion. Test it by converting 255 from decimal to binary, then to hexadecimal, then to base 6. Hint: convert to decimal first as an intermediate step using int(), then use numpy.base_repr() for the output base.

Mission 4: Base-6 Addition

This is the hard one. Write a function add_base6(a, b) that takes two base-6 numbers as strings, adds them, and returns the result as a base-6 string. The challenge: don't convert to decimal first. Implement the addition directly in base 6, digit by digit, with carrying. Remember - you carry when a column sum reaches 6 or more, not 10. This is how Rocky probably learned arithmetic in Eridian school.

Good luck, cadets. And remember - if you ever meet an alien, the first thing you'll want to figure out is how they count. Everything else follows from there.

import numpy as np

# Eridian digit symbols (0 through 5)
ERIDIAN_DIGITS = {
    "0": "ℓ",
    "1": "Ɪ",
    "2": "V",
    "3": "λ",
    "4": "+",
    "5": "∀"
}

# Reverse mapping for converting Eridian back to digits
ERIDIAN_TO_DIGIT = {v: k for k, v in ERIDIAN_DIGITS.items()}

def to_eridian(decimal_number):
    """Convert a decimal number to Eridian (base-6) notation."""
    if decimal_number == 0:
        return "ℓ"
    
    base6 = np.base_repr(decimal_number, 6)
    return "".join(ERIDIAN_DIGITS[d] for d in base6)

def from_eridian(eridian_string):
    """Convert an Eridian number back to decimal."""
    base6_string = "".join(ERIDIAN_TO_DIGIT[ch] for ch in eridian_string)
    return int(base6_string, 6)

# Examples from space exploration
print(f"Days in a year: 365 = {to_eridian(365)} in Eridian")
print(f"ISS speed (km/h): 27600 = {to_eridian(27600)} in Eridian")
print(f"Earth years in a century: 100 = {to_eridian(100)} in Eridian")

# And convert back
print(f"Eridian V+∀ = {from_eridian('V+∀')} in decimal")