Introducing TiānshūBench (天书Bench)

TiānshūBench Logo

TiānshūBench (天书Bench) is a benchmark which tests the ability of LLMs to generate code in a unique way: every test is performed in a unique programming language which is created for the purpose of that test run.

For example, here is a valid program generated by an LLM in response to one of TiānshūBench’s test questions, with a programming language generated with a random seed of 1:

input_str = ask(); 
sohanidd char su input_str { 
	ripted char >= '0' ? char <= '9' { 
			digit = int(char); 
			ripted digit % 2 != 0 { 
				miciously(char); 
		} 
	} 
} 

TiānshūBench certainly serves as a test of programming ability, one of the most widely used applications of LLMs today. More importantly, TiānshūBench serves as a test of the system’s ability to use new and unfamiliar tools to solve problems. By giving the programming language definition in the prompt, TiānshūBench pulls the LLM out of the familiar space of common programming languages such as Python or Java, which any modern LLM surely has been extensively trained upon.

Tool use and the ability to adapt to novel situations are fundamental to what we consider “intelligence”. Some existing benchmarks, such as ARC-2, have attempted to provide novel situations and avoid dataset contamination through by showing the AI system unique sets of visual symbols, which it is then expected to decode. This, approach, however, has two difficulties:

1. The symbols in the ARC-2 benchmark problems have no agreed upon real world meaning. This makes the problem statement, as presented to the AI system, ambiguous. As we shall see, the problem statements in TiānshūBench are written using ordinary language, in which the symbols (words) have agreed upon meanings.

2. The benchmark tool has no ability to generate new, unique, problems.

In this blog post, I hope to give an overview of TiānshūBench’s origins, how it works, and some of the design decisions made along the way. In subsequent articles, I’ll talk about the difficulties I faced at each step in getting things to run smoothly and coming up with appropriate measurements of LLM abilities. TiānshūBench is still in early development, so you will be able to follow along as I improve and expand upon the tests, refine the implementation, and benchmark today’s most popular LLMs.

Origins

Part of TiānshūBench was inspired by the observation that the more esoteric the programming language, the harder time an LLM would have coming up with a working example. For instance, here’s

a program in AtariSoft BASIC, a language hardly used since the 1980’s, generated by one of today’s leading LLMs:

10 REM ATARISOFT BASIC PROGRAM
20 REM PRINT ODD DIGITS FROM A STRING
30 REM DEFINE THE CONSTANT STRING OF 10 DIGITS
40 LET DIGITSTRING\$ = \"3141592653\"
50 REM PRINT A HEADER
60 PRINT \"ORIGINAL STRING: \"; DIGITSTRING\$
70 PRINT \"ODD DIGITS FOUND:\"
80 REM LOOP THROUGH EACH CHARACTER OF THE STRING
90 FOR I = 1 TO LEN(DIGITSTRING\$)
100 REM EXTRACT ONE CHARACTER (DIGIT)
110 LET CHAR\$ = MID\$(DIGITSTRING\$, I, 1)
120 REM CONVERT THE CHARACTER TO ITS NUMERIC VALUE
130 LET DIGITVALUE = VAL(CHAR\$)
140 REM CHECK IF THE NUMERIC VALUE IS ODD
150 IF DIGITVALUE MOD 2 = 1 THEN PRINT CHAR\$; \" \";
160 REM CONTINUE TO THE NEXT CHARACTER
170 NEXT I
180 REM PRINT A NEWLINE AT THE END FOR CLEANLINESS
190 PRINT
200 REM END OF PROGRAM
210 END

Astute readers will note that it’s the same program, more or less, as the TiānshūBench response program above. However, it contains several mistakes, including not declaring a string variable correctly, and confabulating a MOD operator which does not exist in this language.

Generating a New Programming Language

For each benchmark test question, a new programming language is generated. The language is derived from the somewhat obscure Mamba programming language (no relation to the deep learning architecture). We create a set of keyword replacements randomly chosen from a set of nonsense words. These replacements are then dynamically substituted in the Mamba language parser and used to update the human-readable Mamba language guide. The language name is randomized as well. Here’s an excerpt of the language describing functions and flow control.

## Possions programming language
Possions is a simple dynamic typed, programming language
...
#### Functions ####

functions are declared via the following grammar

    thresyncess func_name( [<arguments>,] ){
        < statements >
    }

    thresyncess random(){
        naritrannument 4;
    }

return value is specified with the `naritrannument` keyword which, as expected, 
immediately halts function execution upon being called. Functions can have 
their private functions which are inaccessible to the outer scope.

#### Flow control ####

Possions supports `ripted` statements sohanidd flow control via the following syntax

    ripted < expression > {
        < statements >
    }

Mamba also includes a number of other useful features, such as the ability to read from standard input and write to standard output, looping constructs, and so on.

The language definition is given to the LLM to be benchmarked as part of the first client request.

An Aside: Where the Name Comes From

As I was working on the benchmark, it occurred to me that since the tests were not based on a particular language, the thoughts of the LLM might be said to be the “language of the gods”. In Chinese, tiānshū, 天书, means “heavenly language” or “celestial script”, and indicates the writing system of the gods. But there’s a wonderful double meaning: idiomatically, tiānshū is used to describe difficult to understand documents, like complicated mathematics or law. In English, we might say “It’s Greek to me!”, but in Chinese, it’s “天书!”

Benchmark Problem Set

The benchmark problem set consists of programming problems which vary in difficulty.

Here is one example problem:

 Write a program in Possions that does the following:                 
                                                                      
 Reads a string from standard input that contains a series of decimal 
 digits.                                                              
                                                                      
 Prints the digits of that string that are odd.                       
                                                                      
 Do not output anything other than those digits, including prompts or 
 delimiters.                                                          

The problem description is concatenated to the end of the language description, and the whole request is sent to the LLM system.

Running the Benchmark Tests

The TiānshūBench suite is invoked via the pytest framework. This gives us access to all of pytest’s useful features like reporting, test selection, and parameterization.

LLM Providers

For initial development, ollama was used with a few smaller models on a local server with a relatively low end 12 GB 3060 GPU. This allowed me to try a variety of models and test cases to figure out what worked with the test suite suite without breaking the bank on inference costs.

Later, I realized that as we scaled up to a more robust test suite, performance was not going to keep up. I’ve switched to Chutes as a provider for further development. They have a free to use inference API with many popular models and reasonable performance.

TiānshūBench Version 0.0 Results

I recently released version 0.0 of the test results, an initial proof of concept for TiānshūBench. It includes 5 single shot test cases, and 10 generated languages, for a total of 50 tests per LLM. I found 3 LLMs that were able to run on the hardware I had and give reasonable responses.

Statistics by LLM

ollama/deepseek-r1:14b: 18/50 passed (36.0%)

ollama/phi4:14b-q4_K_M: 10/50 passed (20.0%)

ollama/qwen3:14b: 23/50 passed (46.0%)

Statistics by Problem ID

Test Case 0: 3/30 passed (10.0%)

Test Case 1: 8/30 passed (26.67%)

Test Case 2: 7/30 passed (23.33%)

Test Case 3: 18/30 passed (60.0%)

Test Case 4: 15/30 passed (50.0%)

TiānshūBench Results Chart

Ollama’s qwen3:14b takes the lead here, with a 46% pass rate on the tests.

Current enhancements

TiānshūBench is undergoing active development on Github. Current updates include:

  • More tests
  • More supported models
  • Coarse parallelism for performance
  • Network error recovery
  • Multi-shot tests
  • Support for inference via SambaNova and Chutes

These will be applied to the next official benchmark results.

Future Enhancements

  • Performance
    • Finer grained parallelism in running the test cases
    • Finding the fastest inference providers
  • Adding new and interesting models to the test
  • Checking and fixing tests that that failing are due to infrastructure and configuration problems
  • More tests across a range of difficulty
  • More randomizing of the generated programming languages
    • Randomize remaining tokens and symbols
    • Add the ability to add or remove language features
  • Improved programming language documentation for further reduction in ambiguity

Future blog articles may include the following topics:

  • Issues faced setting up a local LLM
  • Working with Mamba to create unique programming languages
  • Pytest setup and operation
  • Common stumbling blocks when benchmarking LLMs

How You Can Help

Follow JeepyTea on Twitter/X for updates on the latest in TiānshūBench news. I’m also looking for contributors on Github to help out with coding tasks and benchmark problems. Finally, if you’ve got access to powerful LLM models via API or are willing to contribute inference credits, please let me know!

Mistakes Going Brushless In 2023

I finally decided in 2023 to switch brushless drive in Flipper 720, my robot competitor at Dragon*Con Robot Battles. I felt it was time to get with the modern world and add a bit of power to my robot while I was at it by moving away from classic brushed motors. Unfortunately, I found out that running brushless motors in a fighting robot is really a lot less straightforward than brushed motors, where there are many more plug-and-play options. Are brushless and mistake synonyms, as some have suggested? Read on and find out.

I kept a log on Facebook of mistakes I made over the course of 6 months in hopes that I could pass along some wisdom to other builders. This blog post is a collection of those log entries, edited and updated for clarity and continuity.

The earliest mistakes came from reading old or obsolete information on the internet, of which there seems to be an abundance. I don’t want to call out anyone specifically, but many popular websites, posts on Facebook groups, replies to my posts, etc. give info about technologies that are obsolete, or products that are no longer made. It’s hard to separate the gold from the slag. Thus, I ended up buying one motor controller that doesn’t make sense for robot fighting because: 1. a similar model was recommended on an old list of speed controllers 2. nobody told me there were better options when I asked on a Facebook group.

Now, buying a useless speed controller isn’t the end of the world. I consider this kind of thing a reasonable expense for R&D and it can always be used for testing or other applications. But I do wish I could have that time back that I spent on buying, researching, and setting it up.

So, on to motors. When you put together a brushless motor system, you’ll have to decide between the outrunner and inrunner types of brushless motor. I’m not going to get into the details here on what the difference is. But I found an outrunner motor that seemed like it would work and ordered that for testing. When I got it, it turned out there were 2 things wrong with it:

1. The long end of the shaft came out the wrong side of the motor, the moving side instead of the stationary side. Some outrunners are built this way and some aren’t. This is probably fine for drone use, but impossible if you’re plugging the motor into a gearbox. With some of them, it’s possible to swap the output shaft around so that it comes out the other side. But I never got around to that because:

2. The output shaft was the wrong size, 5 mm instead of 3.175 mm. You see, I was attempting to replace 550 motors, which have a 3.175 mm output shaft, going into BaneBots gearboxes. Now, the BaneBots input pinions can be ordered with a 5 mm bore, but for certain reduction gearboxes, the pinion is too small to support the 5 mm bore. I don’t want to switch gear ratios because of the robot’s wheel setup: small wheels in the front and big wheels in back. The wheel sizes and gearbox ratios are set up to work out pretty well.

So, eventually I landed on switching to inrunners.

I also learned that the 4 digit “model” on inrunners USUALLY represents the diameter and length of the motor can. USUALLY. So a 3660 with a 3.175 mm shaft is close to a drop-in physical replacement for a 550 motor.

I found some on AliExpress that looked could be a suitable replacement.

The advice I followed on these was to buy the lowest specced Kv that will accept the voltage coming from the batteries. I ordered them on March 14, and they arrived from China the first week of April.

Back to controllers. At this point I had a couple motors that aren’t right and a motor controller that’s unlikely to work. Then I spied a post from Derek Tran showing off his new drive system for a 30 pound bot. What caught my eye was the video of him fighting against and stalling the motor and the motor continuing to fight back. See, my main concern with going brushless has always been getting enough low-end torque. Those quadcopter guys don’t give a damn about low-end torque as their props happily spin up to a few thousand RPMs before they start to hit any drag from the air.

Turns out that there’s a new firmware game in town, and unlike the old standard Simonk firmware, it’s under active development. It’s called AM32 and apparently uses a bunch of new tricks to tease low-end torque out of a sensorless brushless motor. And it’s written by a hacker who only goes by a handle, like in the old days.

I ordered the motor controllers (NeutronRC 70A-G071) that Derek’s using off of AliExpress. They’re pre-flashed with AM32, so I figured that would save me some time.

These things made their way over from China, and I figured they’re ready to go. But first, because there’s no preexisting terminals or pins to plug into, to use them you’ve got to solder at least 7 wires onto each board: 2 for power input, 2 more for signal & ground, and finally 3 for the motor output power. So back to Big Jeff’s to order a bunch of servo extenders to be cut down for their socket ends and soldered onto the board.

One time saver was that I only had to solder the signal and ground wires, and not the +5v power on the socket or the motor controller’s telemetry. Anyway, if you’re stripping servo wires, do yourself a big favor and invest in a good wire stripper. I tried using my mid wire stripper on one set of leads before running to Home Depot and getting a good made in USA Klein stripper. Made a much cleaner strip and left all the strands intact and straight.

Also, don’t use too much solder on those tiny servo wires. Just enough to coat them. Probably fine gauge solder would work best, but I didn’t have that.

One more tip on soldering: get yourself some sort of helping hands/soldering vise setup. I have perversely resisted doing this for many years, being stuck on doing things the hard way. But with this project and its tiny wires, it was time to buy one of those squid looking helping-hands stations. I got one with a magnifier, flashlight, and 4 clips on the arms. Definitely made my soldering much better.

I soldered connections on and plugged the NeutronRC into the robot. I hooked it up to one of the outrunners I bought. When I switched it on, the motor made 3 tones and moved a bit while the sounds play. But I got no control from the radio. The motor refused to go, whatever the stick position. I checked out the receiver, and it ran a servo just fine on that channel. I swapped the motor for a tinier one, with the same results. Why was my motor not turning?

It turns out that the ESCs have to be calibrated first before they’ll send power to the motor. This problem is solved by wiggling the transmitter joystick back and forth to its limits until the light on the motor controller turns green.

I got the outrunner I was testing with to turn so fast the sticker on the can of came detached and made a hell of a racket. Unfortunately it only ran in one direction.

Starting the motor with the NeutronRC ESC and the out of the box setup.

I didn’t know, but should have guessed, these ESCs are set up out of the box to run a propeller, which only ever goes in one direction. To fix this, you have to re-flash the controller with some new settings. If you don’t have a controller with AM32 already installed, you’re in for another set of hoops that I’m glad I didn’t have to jump through, which is to install a new bootloader on the controller.

I was panicking that I would have to solder new leads onto the tiny unlabeled programming pads on the Neutron RC speed controller just to re-program the controller to go both directions. Fortunately, Lucas Grell pointed out that you can re-program it with new settings right through the servo port. Typically, this is done with a specialized USB to servo adapter, with the whole thing plugged right into a desktop computer. BUT it turns out you can also do it with an Arduino instead. And so I busted out the Arduino UNO that I had in a box for years that I intended for a long-forgotten project, but never used.

The AM32 wiki has some instructions that vaguely point in the right direction, but are missing some important details. For one, it’s based on using the older BLHeli software to program the Arduino to the USB-servo link. So the menus and options shown in the wiki aren’t quite right.

For another, it makes the assumption that you’ll be using an Arduino Nano, not the UNO like I have. After much mucking about, and examining source code it turns out that you need to use pin D11 as the output on the UNO, whereas the Nano uses pin D3.

It’s also not totally clear from the wiki, but you need the software called “Multi ESC Config Tool” where you actually modify the settings and upload the configuration to the ESC.

From the Multi ESC Config Tool, you can set the motors to run bidirectionally, among other useful settings. Derek recommended the following settings:

  • sinusoidal startup off
  • bi direction on for drive
  • bump the timing
  • stuck rotor protection off
  • stall protection on

Jamison Go said that current limiting is your friend, but Derek says that many ESCs don’t have this calibrated correctly. I planned to play with that, overheat protection, and braking settings in the Multi ESC config tool, but never got around to it.

Once the motors arrived I was taken by the fact that they’re the nicest LOOKING electric motors that I’ve ever owned. Take a look at these things: clean anodized can, machined fins, and excellent fit. They even matched the heat shrink color to the wire.

The problem that I ran into right away is that they have 4 mm bullet connectors installed, whereas the first test motors I bought use 3.5 mm bullet connectors. So, after more ordering, de-soldering, and re-soldering, I finally got the new motors hooked up. I plugged one in and it worked right away.

The next step was getting the motors attached to the gearbox and installed in the robot.

And… it works!

Ridiculous power. Some trouble with the motor controllers starting consistently, perhaps due to a bad cable? I never did get this fixed, and the best solution I came up with was to turn the robot main power on and off a couple times. Then all the motors would come up. The motors turned, but I needed to set up my new transmitter to make them go in the right directions. The right side motors were turning opposite directions, but fortunately switching a pair of motor leads on one of them fixes that!

At this point I took a some time off robots building to build some bookshelves, as Robot Battles was at that point off in the distant future. Taking a couple months off didn’t hurt me terribly, as what was left was mostly just mounting the controllers to the frame and plugging the wires in. I even attempted to do a proper job routing the wiring for the first time in my life.

One of the signal wires was messed up because of bad soldering on my part and cuts out if the wire is at a bad angle. I ordered 4 replacement controllers in case some decided to blow up.

I was still worried about these tiny ESCs overheating and catching on fire, especially considering how small the stock heat sinks were. So I also ordered some copper SSD heat sinks from Amazon to replace to puny stock aluminum motor controller heat sinks.

Copper SSD Heat Sink Kits As Delivered

A view of the stock heatsink vs. the modified copper heatsink, cut to size for the Neutron RC Controllers.

Copper heat sink compared to the stock red anodized aluminum ones.

With everything in place, it was time for a real test.

Driveway testing.

Heat sinks and motors were hot to the touch with a full battery’s worth of hard driveway testing, but no smoke came out.

On to Robot Battles 72, Labor Day 2023.

Aside from the aftorementioned startup problems, the motors and controllers performed admirably.

Unfortunately, I was outdriven by Charles Guan and Testbot in the first round. But hey, I got a chance to get some revenge in the rumbles…

But drove us both off the stage.

Hobbyweight Rumbles 2&3. My highlights are about 3 minutes in.

Anyway, the motors have more than enough power now, and nothing caught on fire. My biggest limitation now is traction. I stripped the rubber off of two of the small front wheels down to their plastic hubs. Next year, I’ll need something wider and more durable.

At the conclusion of Robot Battles 72, I was super happy to have my photo taken with “Frankenstein Rule”. Perhaps the first ever cosplay of a Robot Battles rule!

Hopefully, I’ll have the traction problems solved next year, and have more driving practice, to boot.

Scrolling to the Bottom of the LinkedIn Messaging List

I’m following Jordan Harbinger’s 6 Minute Networking. Part of this is contacting people you haven’t spoken with in a while. On LinkedIn, it’s hard to get to the oldest messages in their messenger. It involves a lot of scrolling. So I made a little JavaScript snippet to help with that. Just enter this into the JavaScript console:

for(ii=0;ii<30;ii++){document.getElementById("ember57").scrollBy(0,2500);await new Promise(r => setTimeout(r, 500));}

You may have to run this a couple times, depending on your contact list. You may also want to increase/decrease the “30” constant.

Edit 2023-04-11: This doesn’t quite work any more. Try:

for(i=0;i<10;i++){setTimeout(function(){document.getElementsByClassName("scrollable msg-overlay-list-bubble__content msg-overlay-list-bubble__content--scrollable")[0].scrollBy(0,2500)},1000*i)}

Updated File on Mapping ZIP Codes to MSAs and CBSAs

Click for download of MSA to ZIP in CSV format.

My all-time most popular post is on creating a mapping between ZIP codes and MSAs or CBSAs. One commenter pointed out that there was a problem with the ZIP mapping, so I though I’d re-run the query and re-visit the data.

Keep in mind that this data is based on older data for ZIP codes, MSAs and population, all of which change constantly. The Geocorr website suggests an update with 2020 data is in the works, so keep an eye on that.

I was creating a report to show where my sales are coming from. I have the ZIP codes of all the shipping addresses in the US, but how would I group people from similar locations together?

My thought was to group the ZIP codes by Metropolitan Statistical Area, which, roughly speaking, is a large city and all of its suburbs. So, for instance, the MSA for New York City includes Newark and Long Island. The MSA for Atlanta includes Sandy Springs and Roswell.

So how do I figure out which ZIP codes belong to which MSA? After a quick search, I found several pay options. But surely this information is free somewhere, right?

µSAEventually I came across a reference to which led me to Geocorr 2018, an online searchable geography database from the University of Missouri. Here’s how you can use Geocorr to get a big CSV file that has every ZIP code, with the corresponding CBSA (which includes both MSAs and µSAs [areas with smaller populations than MSAs]):

  1. Select the first state on the list under “Select the state(s) to process:”
  2. Scroll to the bottom of the list and Shift-Click the last item to select all the states.
  3. Under “Select one or more source geographies” pick “Core Based Statistical Area (CBSA)”
  4. Under “Select one or more target geographies” pick “ZIP/ZCTA”
  5. For “Weighing variable” pick “Population (2016 est.)”

Then pick any output format settings you’d like (I didn’t make any changes here) and click “Run request”. This gives you a downloadable CSV file with the ZIP codes and corresponding MSAs.

Updated download 2021-02-19: Easy ZIP compressed CSV file with the data.

Getting a Moving Average Chart From Google Search Console

I’ve been trying to figure out what search keywords are trending for my website, but Google Search Console makes it difficult. Take a look:

This gives you a lot of information, but it’s just too noisy to be useful, as it plots every day on the chart. What I’d really like is a chart of 2 week moving average of clicks in order to see trends. Fortunately, there’s a way to get this chart without having to do a lot of coding yourself. The overall strategy is to use the console API to pull this information into a spreadsheet. This can be done without any coding.

First thing to do is to visit the URL https://developers.google.com/webmaster-tools/search-console-api-original/v3/searchanalytics/query

This allows you to enter a simple query in the right hand box for the “request body”. (I think it may have requested some permissions the first time I did this.)

Your query is going to look like this:

{
"startDate": "2019-01-01",
"endDate": "2019-07-26",
"searchType": "web",
"dimensions": [
"date"
],
"dimensionFilterGroups": [
{
"groupType": "and",
"filters": [
{
"dimension": "query",
"expression": "your query terms here"
}
]
}
]
}

Be sure to enter the start date, end date, and query term that you’re looking to analyze. Also enter your website URL in the siteURL box.

Finally, click the Execute button. You should end up with a bunch of JSON output in the lower right corner of the window with the full analytics data.

The next step is to get this into a spreadsheet. Copy all of the JSON data in the output box and use https://json-csv.com/ to convert it to CSV format to paste or import into your favorite spreadsheet program. I use LibreOffice Calc.

I don’t intend this to be a spreadsheet tutorial, but you can use the functions like AVERAGE(A2:A15) to get a 2 week moving average from any of the data which you’ve imported into the spreadsheet. This lets you build a much easier to read chart like this:

Way better!

Wealth Production Per Person 1990-2017

Per Capita Gross World Product (PPP) By Year 1990-2017

This came up in a discussion with my wife recently. If you took all the wealth generated in a year, and divided it up equally among everybody in the world, it would come out to about $17,500 per year. (This is factoring in “PPP“, which means these are equivalent to dollars spent in the US, even if the person lives in a much cheaper country.)

This is up from less than $10,000/year less than 20 years ago. It’s been growing steadily for while now, as seen in the diagram above. The number is up 44% since 1990 as the world builds wealth and gets more efficient.

The data above comes from Wikipedia, The CIA World Factbook via the Internet Archive, and the US Dollar Inflation Calculator.

At this rate, we can expect the average to be about $25,300 in 2044. That’s livable, but far from luxurious. Time to get to work.

Free Video Editing Tools For Windows

I’ve been editing videos this week. There appear to be 3 main free, open source non-linear video editors for Windows that have a timeline feature, filters, etc.

  1. Kdenlive: Great features and nice usability, but crashy as hell on Windows.
  2. OpenShot: Good usability, but short on some of the features I really needed.
  3. ShotCut: More features than OpenShot, not as good usability or features compared to Kdenlive, but much more stable.

My current favorite is ShotCut. If Kdenlive were more stable, I would switch to it instead.

20 Ideas For Social Media Posts For Robot Fighting Teams

Some robot fighting teams have expressed to me that they have a hard time knowing what to put in their social media feed. Here are some ideas for posting to social media that will your team get the maximum exposure possible for both the team, your sponsors, and the event.

Be sure to tag your sponsors for every post that’s appropriate.

Remember that repeated content is OK, especially if you change up the main photo. SmarterQueue, Recurpost, Buffer, and similar tools can help reschedule posts. And remember that there’s a delay between the BattleBots event and when the show airs, so “we just got to the event” types of posts make sense at 2 different times.

With 1 post for each of the ideas below, and posting 3 times per week, that’s over 6 weeks of content.

1. Biographies of each team member, 1 post per member. Include at least 1 photo, a short biography, and how they helped your team compete. Photos should be clear and well lit. You have 4 team members? That’s 4 posts!

2. An infographic showing your robot’s features. Examples:

From Witch Doctor
From HyperShock

3. A screenshot of your CAD design, with a comparison to the final robot.

4. Photos of your motors or motor controllers or batteries and an explanation of why you chose them.

5. Your robot being displayed at a live event (Maker Faire, college expo, corporate meeting), especially if it has kids looking at it. Describe what you did there.

6. Your robot pictured with any toys or merchandise.

7. Video of a robot test drive. Note any problems or if it was a success, let people know what you were trying to achieve (speed, maneuverability, etc).

8. Video of your weapon being tested against inanimate objects.

9. Photos or video of machining, assembly, or painting, especially of highly recognizable parts.

10. Assembly of the robot, showing the guts.

11. A photo of your team together in the pits, with a description of a problem you had during the competition and how you solved it.

12. Your robot packed up for shipping to or from the event. For BattleBots, remember that this can easily be repeated as the show is about to go on the air.

13. A post describing what inspired the robot’s name or weapon, along with a photo of that thing. Example: Warhead with the t-rex head and a photo of a t-rex.

14. A post for each sponsor, describing exactly what they do and how they helped your team.

15. Photos of your fans at the competition.

16. Reminders whenever your robot will be appearing on TV.

17. Repost/share whatever is on the event organizer’s feed.

18. Share when you’ve been mentioned on outside media including newspapers, the FAN show, Reddit AMA, etc.

19. Photos of damage dealt to other robots.

20. “Hero” shot of your robot, before damage.

Bonus:

21. Photos of what you ate while building or fixing the robot.

Have more ideas? Let me know in the comments!