Skip to content

Instantly share code, notes, and snippets.

@tkwa
Last active July 22, 2019 18:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkwa/f0c82e04e159d83e2321a736c95630f3 to your computer and use it in GitHub Desktop.
Save tkwa/f0c82e04e159d83e2321a736c95630f3 to your computer and use it in GitHub Desktop.
A Guide to Code Golf in TI-BASIC

A Guide to Code Golf in TI-BASIC

This document uses the following conventions:

  • M, N: Integers
  • X, Y: Real numbers
  • Z: Complex numbers
  • L1: A real or complex list

Tokens

There are 243 one-byte tokens in TI-BASIC with the remainder being two bytes. Know the sizes of the most common tokens so you can golf in your head while cooking, taking a shower, etc.

Variables

The following are the ways to reuse subexpressions in TI-BASIC:

  • Ans

    • Holds the value of the last expression
    • Overwritten by storing to a non-equation variable
  • Letter variables A

    • 2 bytes to store, 1 byte to use
  • List variables |LA

    • 2 bytes to store, 2 bytes to use
    • The |L can be omitted when storing to a named list, so named lists are always shorter than L1 - L6 1.
    • Lists can be associated with formulas; store an expression that evaluates to a list to a named list instead of u to save 1 byte.
    • See the Lists section for more information.
  • Matrices [A]

  • String variables: Str1

    • See the Strings section.
  • Equation variables Y1 / u:

    • Relatively expensive: 4 bytes to store, 2 bytes to use.
    • Stores expressions, not values.
    • Y1(W evaluates Y1 temporarily setting W->X, which is sometimes golfy.
    • u, v, w have two rarely used forms: u(N) and the sequence form u(M,N,S). Both set N+1->[recursiven] after evaluation, one of the only TI-BASIC commands with side effects. I have yet to find a case where one of these forms is optimal.

DelVar and ZStandard

DelVar sets numeric variables to zero. Uniquely, the following linebreak can be omitted, making it one byte shorter than 0->.

ZStandard and other zoom tokens set Y to 0, saving one further byte over DelVar Y and making them the shortest way to store a value to a variable.

By CGCC rules, real variables can be assumed to have their default value of 0 at the start of a program, so this is only useful in the middle of a program.

The omitted linebreak after DelVar means a second command can fit in a single-line If statement: see If for an example.

Math Functions

Arithmetic: +, -, *, /, ~

* can be omitted, except in an expression like L1*(L1>X; in this case use L1not(L1<=X.

Integer Parts: iPart(, int(, fPart, round(

iPart( rounds to zero, whereas int( rounds towards minus infinity: {iPart(~.5),int(~.5)} = {0,~1}. This difference can be useful. Ceiling is ~int(~X .

A linear combination of int(X and fPart(X can be shorter expressed in terms of X and int(X), or X and fPart(X).

  • 1>=abs(X can be not(iPart(X.

round( rounds to 9 significant figures. round(X,N) rounds to X digits after the decimal point.

  • To get the first digit of a positive number, one might use int(10^(X/int(log(X. int(10^(fPart(log(X suffers from rounding errors, but int(10^(round(fPart(log(X is correct and saves one byte.

When these commands are called on complex numbers, the real and imaginary part are processed separately. int(3.14-2.71[i]) is 3-3[i].

  • 3=int(real(Z)) and ~3=int(imag(Z can be 3-3[i]=int(Z

Randomness: rand(, randInt(, randNorm(, randBin(, randM(, randIntNoRep(

rand gets a random float in $[0,1]$. randInt(M,N) gets a random integer in $[M,N]$. rand is 1 byte; the others are 2-byte tokens.

  • rand(N is the shortest way to create a new list of length N.
  • int(Xrand is shorter than randInt(0,X.

randIntNoRep(M,N gets the list of integers between M and N.

  • randIntNoRep(M,N is the shortest way to get the list of integers between M and N when order doesn't matter.
  • 2=sum(not(fPart(N/randIntNoRep(1,N checks whether N is prime.

Combinatorics: !, nCr, nPr

nCr and nPr have a precedence higher than (implied) multiplication.

Calculus: fMin(, fMax(, nDeriv(, fnInt(, solve(

Useful in computing advanced mathematical functions; otherwise usually too expensive.

Trigonometric Functions: sin(, sin^-1(, cos(, cos^-1(, tan(, tan^-1(

  • cos(piN maps even N to 1 and odd N to -1; it is shorter than [i]^^2^n or (~1)^N.
  • cos^-1(cos(X takes X modulo $\pi$.
  • tan^-1(tan(X maps X into the interval $[-\pi/2, \pi/2]$.
    • 5/pitan^-1(tan(L1pi/5 maps X into the interval $(-5,5)$, throwing an error when $X \equiv 5 \mod 10$.
  • sin^-1(sin(X is also useful, left as an exercise to the reader.

^^r, ^^o (Degree/Radian tokens)

Radian mode can be assumed as the default, so ^^o is the more useful token.

Hyperbolic Functions: sinh(, sinh^-1(, cosh(, cosh^-1(, tanh(, tanh^-1(

  • tanh(|E9X is the sign function; tanh(|E9{0,~3.14,42 is {0,~1,1}.

  • cosh(sinh^-1(X is $\sqrt{1+X^2}$.

  • The golden ratio is e^(sinh^-1(.5.

Trigonometric and hyperbolic functions do not support complex numbers.

Powers, Inverses, Exponents and Logarithms: ^^-1, ^^2, ^^3, sqrt(, cuberoot(, xroot, 10^(, e^(, log(, ln(, |E

E9 is the standard large number.

Complex Numbers: [i], conj(, real(, imag(, angle(, abs(

conj(, real(, imag(, and angle( are two bytes. Nevertheless, complex numbers are the go-to method of representing 2D coordinates.

angle( can be called on real arguments, returning pi on negative numbers and 0 on nonnegatives. (On monochrome calcs, angle( is unaffected by Degree mode, returning pi for negative numbers whose datatype is real.)

  • In particular, cos(angle(X maps negatives to $-1$ and nonnegatives to $1$ (on color calcs, this works in Degree or Radian mode.)
  • Degree:pi!=angle(~1 returns 1 on color calcs and 0 on monochrome calcs.

A linear combination of real(Z and imag(Z can be shorter expressed in terms of Z and one of conj(Z), real(Z), imag(Z.

  • Example from Flip a number: real(.1Ans)+10[i]imag(Ans) -> 10Ans-9.9real(Ans).

conj is useful for reflecting a point over a line.

  • Map X+Yi to -Yi (flip over the X-axis): conj(Z
  • Map X+Yi to X-X+Yi (flip over the Y-axis): ~[i]conj([i]Z
  • Exchange real and imaginary parts (flip over the line X=Y): ~conj(iZ

Angles: R>Pr(, R>Ptheta(, P>Rx(, P>Ry(

When it is not possible to use a complex numbers for a 2D vector, the angle commands are occasionally useful.

Logical and Comparison Operators: and, or, xor, not(, =, !=, <, >, <=, >=

The or and xor operators have equal precedence; and has higher precedence. Remember to use DeMorgan's laws to eliminate not( and parentheses.

Comparison operators have a tolerance because their arguments are round(ed first: 1>=1+4.99|E~10 is 1, but 1>=1+5|E~10 is 0.

Comparison operators do not chain together: X=Y=C is equivalent to (X=Y)=C. This is only useful when C is not always 0 or 1, since otherwise X!=Y xor C would be the same length.

Comparison operators do not support complex numbers.

Statistics

  • variance(L1 and stdDev(L1 return zero if all elements of L1 are equal.
  • Remember the two-argument (weighted) forms of mean( and median(.
  • When a challenge asks for multiple statistical properties of the same list, consider using 1-Var Stats or 2-Var Stats .

Misc. math: min(, max(, lcm(, gcd(, remainder(, sub(, %

min( and max( compare complex numbers by their absolute values.

remainder( is two bytes. lcm( , gcd(, and remainder( require their arguments to be positive integers in the range $[1,10^{12})$. lcm(L1) and gcd(L1) are not valid, so you must do 1:For(N,1,dim(L1:lcm(Ans,L1(N:End, preferably reusing an existing loop.

sub( and % (a postfix token not available in the program editor) are 2 bytes. They both multiply their argument by .01, which can occasionally save bytes. For example:

  • not(fPart(N/4/4^not(fPart(sub(N gets whether year N is a leap year.

Lists

Most math commands automatically vectorize over lists.

augment(

augment( is 2 bytes. Can be useful for constructing lists of a fixed length.

  • Append an element to the end of a list: augment(L1,{X->L1

DeltaList( and cumSum(

DeltaList( and cumSum( are extremely useful, despite being 2 bytes.

  • {4,8,15,16,23,42} can be cumSum({4,4,7,1,7,19.
  • DeltaList(cumSum(L1 removes the first element of L1.
  • cumSum(DeltaList(L1 is equivalent to DeltaList(cumSum(L1))-L1(1.

Since the output of DeltaList( is one element shorter, use augment( to match dimensions; e.g., not(L1 and ~5>DeltaList(augment({0},L1 finds where L1 is 0 or decreases by at least 5.

Fill(

Generally inferior to 0L1+X, unless a list variable of the right dimensions is already available.

seq(

Use seq( only for commands that are not vectorizable.

seq( cannot be nested; use u or, if possible, summation( instead.

  • Remove the Nth element of a list: seq(L1(A-(A<N)),A,2,dim(L1

SortA( and SortD(

One- and two-argument forms can both be useful.

Matrices

Matrices are expensive to define, index, and manipulate. When your matrix has two rows, consider using a complex list instead.

Strings

Strings are expensive to index and manipulate, since inString(, sub(, length( and string variables are 2 bytes and require quote marks. 70% of the time, it is optimal to just convert an input string to a list at the start of a program:

seq(inString("VXLCDM",sub(Ans,N,1)),N,1,length(Ans

If you need a For( loop anyway and only use one character at a time, store to a real variable inside the loop:

inString("BCDEF",sub(Str1,X,1->N

Empty strings cannot be used in string commands (except =, !=). See Routines for the shortest way to build strings.

inString(

inString(Str1,Str2 returns the first position of Str2 as a substring of Str1. inString(Str1,Str2,N starts searching at position N in Str1, which can be useful in the rare case when strings are not converted to lists.

inString( returns 0 for no match, which means the first character of Str1 can often be left off.

Str1 can be permuted to simplify expressions later on in the program.

Input

Prompt and Input

Prompt and Input do not update Ans. Input used on its own prompts the user to select a point on the graphscreen, then sets the X and Y variables accordingly.

getKey

Arrow keys and DEL (23) are the only autorepeating keys.

The arrow keys <, ^, >, v are codes 24, 25, 26, 34.

  • Tell if an arrow key was pressed: K=34 or 2>abs(K-25

  • Move a cursor around the screen: iPart([i]^int(168ln(K-1 maps the <, ^, >, v arrow keypresses to -1,i,1,-i and 0 to 0. Using 20 instead of 168 maps to -1,i,1,-i.

  • Read a single number key:

    • 1-9: remainder(K,13
    • 0-9: remainder(remainder(K,13),11
    • 0-9, returning the key code on non-numeric keys: K(102!=K)-remainder(K,13)(2>abs(5-abs(5-abs(K-83

Home Screen Output

Formatting Commands: >DMS, >Dec, >Frac, >Rect, >Polar

Rarely useful unless they exactly fit a challenge.

Graph Screen Output

Pxl-On(, Pxl-Off(, Pxl-Change(, etc.

To decide whether to use graphscreen points or pixels:

  • pxl-Test( exists, but pt-Test( does not.
  • Pixel commands accept only positive integers in $[0,62] \times [0,94]$, whereas point commands automatically round to the nearest pixel and do nothing if it would be offscreen.
  • When using points, keep in mind the cost of a friendly graphing window; if possible, rework the rest of your program to avoid changing the window.

Flow Control

In all boolean commands, the calculator interprets real 0 as false, and nonzero reals as true.

If

A one-line If statement saves 4 bytes over If:Then:End, and 6 bytes over If:Then:Else:End.

If is especially handy for leaving a value in Ans.

If C becomes false during If C:Then:[body]:End, use While C:[body]:End instead.

Sometimes, multiple If statements can be combined in non-standard ways.

  • Short-circuiting not(C) or D:

    If C
    If D
    [body]
    
  • If:Else:

    If C:Then
    [body1]
    End
    If not(C) or not(D:Then
    [body2]
    End
    //can be
    If C:Then:
    If D:Else
    End
    

While and Repeat

A While C loop checks if C is true, then runs the body while C is true. A Repeat C loop always runs the body at least once, stopping when C is false.

When writing While C or Repeat C, consider whether Repeat not(C or While not(C would fit better.

For(

For(X,A,B:[body]:End evaluates its bounds once, then repeatedly executes body, and increments X until it passes the bounds. Understand why:

  1. For(X,1,10:End:X returns 11
  2. 10:For(X,1,Ans:0:End:X returns 11
  3. For(X,3.5,1,~1.5:End returns .5
  4. For(X,1,10:20->X:End returns 21
  5. For(X,1,10:~5->X:End is an infinite loop

Remember the fourth "step" argument; it is often, but not always, useful.

  • For(X,1,|E9 counts up near-infinitely.

Lbl and Goto

Goto and Lbl are niche commands, since the If, Lbl, and Goto lines take a minimum of 8 bytes.

IS<( and DS>

Also rare, since a For( loop usually accomplishes the intended purpose in fewer bytes.

prgm

Subprograms are often an alternative to Y1 or u. By PPCG rules, splitting code into two files costs one additional byte.

Since all variables are global, recursion has limited utility, but it is still sometimes the answer.

Techniques

General tips

Eliminate syntax characters

Parentheses, linebreaks, and variable access waste bytes without adding much meaningful code or data to your program. An expression, especially an arithmetic one, can often be shorter when condensed into one line. When an expression contains an opening parenthesis, consider if a function can go there instead---it's essentially free. If there are closing parentheses, try to rearrange the expression so they can be removed.

Hashing / Brute Force Search

To generate functions that map $K$ possible inputs into the interval $[0,N-1]$ where $N^K &lt; 10^{12}$ or so, use an expression of the form

int(NfPart(Mf(X

where $M$ is a large number on the order of $N^K$ found through brute-force search, and f( is a sufficiently "random" function (ln(, sqrt(, tan(, etc.). Use this program to generate M.

In particular, a function from ${1,...,K}$ to $[0,N-1]$ is usually shorter as a hash. Suppose we want to find the length of the Roman Numeral representation of an integer $1 \le X \le 9$; the following expressions are equivalent.

  • List indexing (22 bytes): {1,2,3,2,1,2,3,4,2:Ans(X
  • String indexing (19 bytes): expr(sub("123212342",X,1
  • Base conversion (17 bytes): int(10fPart(.012321234210^(X
  • Arithmetic (16 bytes): min(X,1+min(19-2X,abs(X-5
  • Hashing (12 bytes): int(6fPart(Mcosh^-1(X, where finding the 7-digit number M is left as an exercise.

Considering different f and slight variations on this form can save a byte or two, or allow mapping 0 to a nonzero value.

  • To map to {0,1} with a distribution of 63.7% 0s and 37.3% 1s, use int(sin^-1(fPart(Mf(X.

When N is fairly small but K is too large, consider reducing the domain by using clever math. Failing that, it's possible to chain hashes together in a technique similar to modulo chains.

int(NfPart(KfPart(f1(int(LfPart(f2(int(...

Fully optimizing hashes with large K often involves exploiting even more degrees of freedom, and can be computationally infeasible. Use the optimal expression generator if your search space is fairly small, say under 6 bytes.

When N is fairly small but K is too large, consider reducing the domain by using clever math.

Advanced use of DeltaList( and cumSum(

Uses with log(

  • 10^(DeltaList(log(L1 gets ratios between elements.
    • 2=10^(DeltaList(log(L1 gets where an element is double the next element.
  • Likewise, 10^(cumSum(log(L1 is the nonexistent cumProd(.

Chaining DeltaList(

  • DeltaList(DeltaList(L1 gets the second-order differences.
  • not(DeltaList(DeltaList(L1 checks for three-term arithmetic progressions in L1. If the domain of L1 is restricted, this sometimes means having three adjacent equal elements.
  • max(2=10^(abs(DeltaList(log(abs(DeltaList(DeltaList(e^(L1 searches for the pattern {X,Y,X,X} or {X,X,Y,X} in the integer list L1 with elements in $[0,20]$.

Math on Booleans

  • If X is known to be nonzero, If X>1 can be If log(X.
  • 1+NC can be (N-1)^C when N is a constant.
  • cos(piC maps 0 to 1 and 1 to -1; it is shorter than [i]^^2^n or (~1)^C or 1-2C.

Miscellaneous Golfs

Literals

  • {1,2,3,4} can be timeCnv(93781. timeCnv( is 2 bytes, so this doesn't always save bytes.
  • {5,5 can be dim(identity(5

Finding whether X is in a range

These substitutions often let you avoid storing X to a variable.

  • X>=4 and X<8 (7 bytes) -> 1=int(4^^-1X (5 bytes)
  • X>=1 and X<8 (7round(^^-1(+X(10 bytes) ->1=int( bytes; still net -1 when you can eliminate ->X)
  • X>=4 and X<=8 (7 bytes) -> 2<=abs(6-X (6 bytes)

Routines

Building a String

"
While C
Ans->Str1
...
<next part of string>
If length(Str1
Str1+Ans
End

Footnotes

  1. Exception: 1-Var Stats and 2-Var Stats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment