52.14.221.113
--:--:--
Navigation
Furcadia Numeric Systems
Author:Artex
Date:28-Dec-2008 (Updated: 28-Dec-2008)

Overview

Most of the information on Furcadia comes in numbers: coordinates, item IDs, dragonspeak lines, variables, the color code - all of this is passed in form of numbers. Unlike some other systems that use binary or decimal numeric systems to pass data, Furcadia uses a numeric system of its own and thus, in order to understand the Furcadia Communication Protocol and/or some of the commands it uses internally, it is necessary to understand how this Furcadian numeric system works.

Here we are going to discuss the base95 and base220 numeric systems - both of which are in use by Furcadia at the time of writing.

General Numeric Systems and Digits

Before getting into Furcadia numbers, let's first look at numeric systems we currently use when it comes to computers: Decimal, Hexadecimal and Binary. If you already know how these work, feel free to skip to the next section.

In the real world, when it comes to numbers, we always use the Decimal numeric system, otherwise known as base10. The base10 system uses 10 digits that we all know well: 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9. This means that we can represent 10 numbers with one digit - numbers from 0 to 9. Numbers higher than 9 will be using more than one digit: 10 (2 digits: 1 and 0), 25 (2 digits: 2 and 5), 128 (3 digits: 1, 2 and 8), etc.

Let's have a look at another numeric system, commonly known to those who program as Hexadecimal (also called base16). This numeric system differs from decimal with how many digits it supports. While base10 system supports 10 digits, base16 supports 16 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E and F. In this numeric system, we can cram 16 numbers into one digit rather than just 10, and while it seems like an insignificant difference, in bigger numbers it can save you several digits and make the number's length look smaller (i.e. FF in base16 is 255 in base10 - 1 digit saved, FFFFFF in base16 is 16777215 in base10 - 2 digits saved). That is for base16. There are also base64 numbers which, needless to say, are a lot more compressed than base10 and base16 together! For a comparison among these systems, check out the table below and see for yourself!

By now, it might seem pretty confusing to have leters (ABCDEF) as digits - they are leters afterall - not numbers! The answer to that is simple: we never made a special look for digits above 9, so there is no special-looking "figure" to visually represent those digits, but since leters are also figures, and they also come at a certain order (first A, then B, C, D, etc), we can safely substitute these extra digits with leters instead, and it will not ruin the meaning of a number. To complicate the matter - drawn figures don't have to be the only things used for digits: computers are using electricity and light, which have only two states (on and off) and convert these states into numbers by assigning the "off" state the digit 0 and "on" state the digit 1. That's how the Binary numeric system was "born". This system is now responsible for absolutely everything you see on your computer screen! Pretty cool, no?

Base95 Numeric System

Now that we know that base10 can represent 10 numbers in a single digit and base16 represents 16, it would probably be no surprise that "base95" allows 95 numbers in a single digit. But hey, how do they do this? Afterall, there are only 10 digits and 26 leters in the alphabet!..

Using the ASCII characters as digits

ASCII table with base95 coverage

Meet the ASCII Table (and if you don't know what this is, you should probably do yourself a favor and look into it first). This is a table of characters which can be used as "digits" - each of these characters takes up one byte when we transfer data across the network. This would make one realize that using the decimal numeric system (which consists only of only 10 digits out of the potential 256) here to transfer numbers, would waste a lot of bandwidth! While being able to cram 256 numbers in one byte, we would be using less than 4% of that potential! The other 96% will still be sent across the network, but never used for anything program-wise! Isn't it a bit wasteful?..

Why base95 and not base256?

So what made DEP choose the base95 system if it doesn't even cover half the ASCII table? Why not use a "binary" mode and cover the entire table to achieve maximum potential? While I don't have the official answer at hand, here are some of the reasons I would rather stay with the more simplistic protocol and base95:

  • The communication protocol is a lot easier to learn without any kind of official specifications when it is written in a more "human" form than numbers being everywhere. This allows people to easily learn the ropes and make 3rd party tools for Furcadia such as more enhanced dream/patch editors or proxies.
  • When the protocol is so simple that you can easily and fully connect by using a simple telnet client, it is a lot more simple to debug any possible errors in communication than using packet capture tools and decoding the numbers received. The chances for such errors to occur are also lower as a result.
  • Operating Systems often have built-in features to easily parse and manipulate text. Thus, making a protocol that closely resembles simple text would make it possible to use the OS for tasks a programmer would otherwise have to write a separate system for.
  • Simple systems are a lot easier to maintain and enhance than sophisticated ones.

Conversion

When it comes to converting a decimal digit into a base95 digit (which is an ASCII character), all you have to do really is to add 32 to the digit. This would skip the first two rows in the ASCII table, which contain the Control Characters - those are unreadable and some of them carry special tasks in text. One should also not forget that we can only use 95 digits in this numeric system, so we will (technically) be unable to use characters beyond the ~ character. Anything higher than this digit will be using two digits - just like anything that is higher than 9 in base10 will start using two digits: 10 (1 and 0).

Converting two digits and more will involve a little math, since we will be splitting a base10 number into several base95 digits - each system with a different amount of available digits.

From base10 to base95

  1. Assume base95num as empty string ("").
  2. While base10 number is above zero:
    1. Do (base10num % 95) + 32 and store in memory (% being modulus).
    2. Convert stored result into an ASCII character and add it to the beginning of base95num (i.e.: "Xxxx").
    3. Do base10num / 95 and store the result in base10num without remainder!
  3. base95num now contains the base95 representation of the base10 number.

From base95 to base10

  1. Assume base10num as a number that equals 0.
  2. Assume multiplier as a number that equals 1.
  3. For each character in base95num FROM RIGHT TO LEFT.
    1. Convert ASCII character into its numeric value.
    2. Do (char - 32) * multiplier and add the result to base10num (base10num = base10num + result).
    3. Multiply multiplier by 95 and store the result within it (multiplier = multiplier * 95).
  4. base10num now contains the base10 representation of the base95 number.

You might want to check the code samples for real languages - to someone who writes code it may appear a lot simpler than reading what was written above.

Base220 Numeric System

Officially first utilized on 31-Jan-2008 (The FOX Update), the base220 numeric system was introduced in order to succeed the base95 system. It was implemented in newer/upgraded instructions in the protocol such as the color string, movement, character tracking instructions and wherever character's User ID was necessary. For now obvious reasons, base220 is more than twice as optimized as its predecessor, as it can contain 220 numbers per digit. Its ASCII coverage is as follows:

ASCII table with base220 coverage

There are several caveats to remember in order to avoid confusion between base95 and base220:

  • In base95, number 0 is a space character (32 or 0x20). In base220, number 0 is the # character (35 or 0x23)!
  • In base95, the least significant digit is on the right side (i.e. "   '" = 7). In base220, the least significant digit is on the left side (i.e. *### = 7)!
  • Specifically in base220, the last digit is 254 (0xFE) and not 255 (0xFF). Digit 255 is off limits!

Conversion

The conversion between a base220 digit and a decimal digit is similar to base95 except for the offset that you add to the number. While in base95 it was 32, here you need to add 35. The last digit is also not digit 127 (0x7E), but 254 (0xFE). Multiple digits are done the following way:

From base10 to base220

  1. Assume base220num as empty string ("").
  2. While base10 number is above zero:
    1. Do (base10num % 220) + 35 and store in memory (% being modulus).
    2. Convert stored result into an ASCII character and add it to the end of base220num (i.e.: "xxxX").
    3. Do base10num / 220 and store the result in base10num without remainder!
  3. base220num now contains the base220 representation of the base10 number.

From base220 to base10

  1. Assume base10num as a number that equals 0.
  2. Assume multiplier as a number that equals 1.
  3. For each character in base220num FROM LEFT TO RIGHT.
    1. Convert ASCII character into its numeric value.
    2. Do (char - 35) * multiplier and add the result to base10num (base10num = base10num + result).
    3. Multiply multiplier by 220 and store the result within it (multiplier = multiplier * 220).
  4. base10num now contains the base10 representation of the base220 number.

Likewise, if this text is too confusing, you can find a (hopefully) cleaner version in one of the real code samples.

Code Samples

The following samples should theoretically work in real programming languages and help you convert numbers to/from Furcadian systems. You are free to use the code all you wish, but don't hold me responsible if it messes up. Giving credit where credit is due is also a good idea. ;)

JavaScript

var base95  = new Object;
var base220 = new Object;

/** Base95 Numeric Conversion **/
base95.atoi = function( b95num )
{
	var b10num = 0;
	var mult = 1;

	// Going from last to first character.
	for( var i = b95num.length; i > 0; i-- )
	{
		b10num += ( b95num.charCodeAt( i-1 ) - 32 ) * mult;
		mult *= 95;
	}
	
	return b10num;
}


base95.itoa = function( b10num, size )
{
	var b95num = '';

	// Size is optional in case you expect a static length.
	if( typeof(size) == 'undefined' )
		size = 0;
	
	// Preventing negative numbers
	if( b10num < 0 )
		b10num = 0;

	while( b10num > 0 )
	{
		b95num = String.fromCharCode( ( b10num % 95 ) + 32 ) + b95num;
		b10num = Math.floor( b10num / 95 );
	}
	
	// This portion is the optional zero-fill. This makes sure the number
	// has a fixed amount of digits if you specify it.
	if( size > 0 )
	{
		var fillWith = ( b95num.length < size ) ? ' ' : '~';
		var fillSize = size - b95num.length;
		
		// If the number is bigger than the length can contain, rise it
		// as much as possible, but keep to the length.
		if( fillSize < 0 )
		{
			b95num = '';
			fillSize = size;
		}
		
		while( fillSize > 0 )
		{
			b95num = fillWith + b95num;
			fillSize--;
		}
	}
	
	return b95num;
}


/** Base220 Numeric Conversion **/
base220.atoi = function( b220num )
{
	var b10num = 0;
	var mult = 1;
	
	for( var i = 0; i < b220num.length; i++ )
	{
		b10num += ( b220num.charCodeAt( i ) - 35 ) * mult;
		mult *= 220;
	}
	
	return b10num;
}


base220.itoa = function( b10num, size )
{
	var b220num = '';
	
	// Size is optional in case you expect a static length.
	if( typeof(size) == 'undefined' )
		size = 0;
	
	// Preventing negative numbers
	if( b10num < 0 )
		b10num = 0;

	while( b10num > 0 )
	{
		b220num += String.fromCharCode( ( b10num % 220 ) + 35 );
		b10num = Math.floor( b10num / 220 );
	}
	
	// This portion is the optional zero-fill. This makes sure the number
	// has a fixed amount of digits if you specify it.
	if( size > 0 )
	{
		var fillWith = ( b220num.length < size ) ? '#' : String.fromCharCode( 254 );
		var fillSize = size - b220num.length;
		
		// If the number is bigger than the length can contain, rise it
		// as much as possible, but keep to the length.
		if( fillSize < 0 )
		{
			b220num = '';
			fillSize = size;
		}
		
		while( fillSize > 0 )
		{
			b220num += fillWith;
			fillSize -= 1;
		}
	}
	
	return b220num;
}

Python

def base95_itoa( n, zero_fill_digits = None ):
	# NOTE: base95 numbers' order is little-endian - smallest number last!
	
	# Not allowing negative numbers.
	if n < 0:
		n = 0
	
	b95int = ''
	
	while n > 0:
		chr    = n % 95
		b95int = b95int + "%c" % ( chr + 32 )
		
		n /= 95
	# /while
	
	# Applying zero-fill as necessary
	if zero_fill_digits != None:
		# If the amount of digits we've got is bigger than requested, return
		# the maximal amount for the amount of digits instead!
		if len(b95int) > zero_fill_digits:
			return atoi( ('~'*zero_fill_digits) )
		
		b95int = ' '*(zero_fill_digits - len(b95int)) + b95int
	
	return b95int
# /base95_itoa()


def base95_atoi( b95int ):
	# NOTE: base95 numbers' order is little-endian - smallest number last!

	n = 0
	m = 1
	
	for chr in b95int:
		n += ( ord(chr) - 32 ) * m
		m *= 95
	# /for
	
	return n
# /base95_atoi()


def base220_itoa( n, zero_fill_digits = None ):
	# NOTE: base220 numbers' order is opposite of base95! "10" means "01"
	
	# Preventing negative numbers.
	if n < 0:
		n = 0
	
	b220int = ''
	
	while n > 0:
		chr      = n % 220
		b220int += "%c" % ( chr + 35 )
		
		n /= 220
	# /while
	
	# Applying zero-fill as necessary
	if zero_fill_digits != None:
		# If the amount of digits we've got is bigger than requested, return
		# the maximal amount for the amount of digits instead!
		if len(b220int) > zero_fill_digits:
			return atoi( ('\xfe'*zero_fill_digits) )
		
		b220int += '#'*(zero_fill_digits - len(b220int))
	
	return b220int
# /base220_itoa()


def base220_atoi( b220int ):
	# NOTE: base220 numbers' order is opposite of base95! "10" means "01"

	n = 0
	m = 1
	
	for chr in b220int:
		n += ( ord(chr) - 35 ) * m
		m *= 220
	# /for
	
	return n
# /base220_atoi()

Negative Numbers

Perhaps in the entire Furcadia, the only place you can see negative base95 or base220 numbers would be in the color code - species setting. While normal species Go from 0 and upwards, localspecies can go below 0: -1 and downwards. The "signed" version of localspecies can otherwise be used as an unsigned - positive number (just like everything else) - the first localspecies would be 219 and from there, downwards. Another method is to take the positive number and subtract it by 220 so the first localspecies (219) becomes -1. 218-220 would be -2, etc. As you can see - not necessarily needing signed base95/220 numbers here.

Numeric System Comparison

The following table shows the various numeric systems and their coverage - how many numbers can they represent per digit (or per byte if we transfer each digit as a byte) for the sake of comparison.

System Name #/1 digit #/2 digits #/3 digits #/4 digits
Binary24816
Decimal10100100010000
Hexadecimal16256409665536
base6464409626214416777216
base9595902585737581450625
base2202204840010648002342560000
base25625665536167772164294967296

Base Converter

If you want to verify your own code for number conversion or a fast way to convert without the need to make scripts or programs for this, here is a small tool to help you (assuming your JavaScript support is intact):

Decimal:
Base95:
Base220:

NOTE: In case of invalid base95 or base220 numbers, the script will not update the fields!

Frequently Asked Questions

This section addresses commonly asked questions that were not addressed or are not meant to be addressed on a global scale.

None yet