Sunday, October 31, 2010

Free web hosting and "new" website

I had a small website up for a while, but Geocities and my ISP have both discontinued their web hosting services. As an experiment I was looking for a new host that was a completely free replacement. The best thing I found so far is:

http://www.awardspace.com

The basic features in a nutshell are:

Total file space: 250 MB
Maximum file size: 2 MB
Monthly Bandwidth: 5 GB
FTP/PHP/MySql included
No Ads.

I had actually registered an account with them several years ago when it was all still in beta, and the test page I made was still working as I left it. So, I was pleasantly surprised to see they have their act together (so far) and have improved the service since I last saw it.

Here's a couple limitations I ran into and work-around's:

1. Some files types are restricted. Though I can see the point of restricting high bandwidth file types like .mov, as well as files with security issues like .exe. They don't want to become the next Napster and get shut down.
  • mp3 file types are not allowed. I used the open source alternative "ogg." In Linux, I found a free program called "soundconverter" that made it easy to do a batch conversion from mp3 to ogg. (The program did pop up an error when first lauching, but worked perfectly otherwise).
  • zip files are not allowed. I used the .bz2 compression format instead. Or you can drop the .zip extension from the file.
2. The marketing about domains is a bit misleading I think. It says it the free package has "1 domain" included and I initially thought "wow, they throw in a free domain name?!" The answer: no. You either have to pay for a domain name, or piggy back off a handful of generic domains like atwebpages.com.

3. If using FTP, probably using a FTP client like "filezilla" would probably work better. On a large batch of file transfers I hit a couple times where the FTP stream was cut off. This is not a big deal though if you have an FTP client that will automatically reconnect and retry the file transfers.

Still this host is a big improvement from what I had been using. On both my previous free hosts, I used to be bound by a 10 MB limit, which is hard to do much with. :)

My website is now at:


Thursday, October 21, 2010

Wacom: using pressure

Tonight I was experimenting with the pressure sensor on the Wacom tablet. I didn't realize it had pressure until I was reading more about it.

For Gimp, I saw a note that it can be turned on by changing all the settings under:

Edit -> Preferences -> Input Devices -> Configure Extended input devices

And set all the Wacom properties listed to "screen." Then save. I'm not sure why that's not turned on by default. Otherwise the stylus will act just like a mouse -- on or off.

So. I tried taking a more "painterly" approach, playing with the pressure. I still don't have the hang of it, but I'm already liking it better than paints. This picture is based off of an older painting I never finished, and eventually lost. It's a person staring at a rock. :)



It's man versus rock, in the ultimate staring contest! ;)

Saturday, October 16, 2010

Wacom Tablet

I'm testing out a Wacom tablet that my brother loaned me. It's pretty cool! If I can get the hang of it, I'd like this a lot better than using paints. I've never gotten the hang of acrylics (they dry too fast on me). That, and I have an undo button, and no cleanup. :)

Here's the first sketch:

I couldn't figure out how to do outlines, and I had more luck turning the opacity down to between 20% to 30%, so I could make a couple quick passes at each line. I rarely draw the line I want on the first try. I use a "chicken scratch" approach, and add layers of different colors. I started with large brush and gradually turned the brush size smaller, working mostly light to dark. For software I used GIMP (a free Photoshop alternative), using the airbrush.

Though it's a bit washed out. I bumped the contrast to make it look more like what I had in mind. With digital pictures, it's nice how easy it is to manipulate the image:



Friday, October 8, 2010

Growing Peppers, and a spicy treat

Well, I grew a few different kind of peppers this year, and will miss them during the winter. The plants are winding down now that fall is here.

The jalapeƱo is a great pepper. It's moderately spicy with a great flavor for pizzas, and mexican food. It's about 2,500 - 8,000 on the scoville heat scale.


The habanero, extremely spicy with a sweet "fruity" flavor. You don't want to eat these things straight, or you will be in pain. They are 100,000-350,000 on the scoville scale. But they are great mixed with other foods:


And the hottest chili in the world, the naga jolokia. I will probably dry these, and use crushed, since they are about 1,000,000 on the scoville scale. That's approaching pepper-spray strength.


I have found a great use for the habaneros. If you like Indian food or spicy food, you might like this recipe. It's a bit like a crunchy popcorn snack but would also be a great side dish.

Roasted Chick Peas

Drain 1 can of chick peas (garbonzo beans) and rinse in a large bowl. You can roll the beans against each other, and all the skins will come off. These float to the top and can be poured off through a strainer. You don't have to remove the skins, but I think they cook up a bit crunchier without them.

Put the chickpeas in a small dish. Mix with 1-2 tablespoons of olive oil, then sprinkle with:

Curry
Salt
Black Pepper
Cinnamon
1-2 chopped Habanero peppers

Cook at 450 F, for 25 minutes.

Sunday, September 19, 2010

Origami in 3D


I while back I was thinking about Origami, and the limits of working in three dimensions.

I realized that if you start with a rigid piece of paper, and fold it back on itself, this can create a layered buffer of paper that can be tapped or stored. So, a rigid sheet can be converted into one of virtually limitless elasticity. That means any form that is possible within traditional sculpture (clay, metal, etc) can be built from a single sheet of uncut lamina.

I ran across work by origami artist Joel Cooper that demonstrates this potential on an amazing level.





From Joel Cooper:
http://joelcooper.wordpress.com/2008/08/19/faq/




From Joel Cooper: http://www.flickr.com/photos/origamijoel/sets/72157606262466165/

Sunday, September 5, 2010

Robert Lang explains origami

Robert Lang explains the principles behind origami, origami design software, along with practical applications in space, medicine, and auto industries (18 min).




One thing that is not covered, is that origami folding can construct the same point sets that can be constructed with a marked-ruler and compass. For example, how to trisect an angle:


Monday, August 16, 2010

Pythagorean Puzzle

I like wooden toys, puzzles, and mechanical devices. The other day, I was looking at a simple proof of the Pythagorean Theorem. The idea behind the proof was incredibly simple, but the simplicity was obscured with the mathematical notation and formalism. But I noticed it could be directly translated into a mechanical device. So, I thought I'd make a toy out of it. The idea is it might make the concepts a little more clear, without the clutter of notation.

The general idea behind the proof is this:

Suppose I have a rug and place it flat on the floor of an empty room. If the rug is too small for my tastes, can I cover more of the floor by moving the rug around (on the floor)? Not really. Regardless of where I put the rug, the same total area of floor will be covered, the same total area will be uncovered.

So, imagine four rugs that are all the same size, and shaped like right triangles. The black space is the uncovered floor:



Now, I can rearrange the rugs, in another pattern:



But either way, the same area of floor is uncovered, regardless of how I move the rugs. So, the black area a2 + b2 has the exact same area as black area c2. So,

a2 + b2 = c2


But also, the axa, bxb, cxc squares are formed directly with the sides of the triangles. In other words, a and b are the length of the triangle's legs, and c is the length of the hypotenuse.

So, this is a visual, mechanical demonstration of the Pythagorean Theorem, for an arbitrary right triangle. :)

Below are the steps I used in making the prototype:

First I started with a large sheet of 1/8" hardboard, and cut a 1" strip. Though, in hindsight, it would have been easier to just find a precut, plained 1" x 1/4" strip.




Now, there will be three basic layers: top middle and bottom.

The top layer can all be cut from the long 1" strip. I used 2"x1" triangles. So the inside of the frame will be 3" and the outside 5". The four small 1"x1" squares will be corner supports between the bottom and top layers. The three small 1/2" x 1" rectangles will be used for sliding tabs (handles).

The middle layer (left bottom) will need to be slightly thinner so that it can slide freely between the corner supports. I just took off a hair with a flat file. The bottom layer (right bottom) will need a slot cut that intersects points (0,0) and (2,1), if the origin is at the lower left. I just used a drill and coping saw. Any thickness for the bottom is fine.


Roughly assembled:


The yellow triangle will slide along the slot in the bottom layer. The back tab will connect to the yellow triangle with screws (passing through the slot). These will need to be attached at the same angle as the triangles:



Glue the green and yellow triangles to their middle layer supports. Use two small screws to prevent rotation. It's probably best to drill pilot holes for these small pieces.:


Also, I cut a thin strip of felt, and glued to the insides of the slot. This smooths the slide mechanism, and looks a little better. After dry, then cut flush with a razor blade.

The sliding yellow triangle can be attached first. This will determine the position of the frame and other pieces. (backside)


Then add corner supports (which will be cut to size at the end). The only thickness that really matters, is that the middle layer is slightly thinner than the corner supports.


The side tabs and red blue triangles will sit on top of the middle layer, and allow the pieces to slide vertically and horizontally. Glue both tabs and triangles to the middle layers.


Personally, I like the natural wood toys better. But, since this is a wooden toy for kids, I picked bright simple colors. :)

For a children's toy, I think the squares should have been purple, and the frame should have been black. Since I think it's easier to focus on purple than black (a void), and is often a favorite color. But I didn't have any purple felt on hand. Though, black doubles as a chalk board.

(I actually painted everything at the first step, but had to touch up scratches afterwards.)


After painting everything, use a flat bastard file so that edges are clean and sharp.


Then, apply drips of glue to the corner supports, and adjust the frame so that both triangle positions are flush. Cut excess off when dry. I rounded the tabs off with a Dremel tool, and painted the white labels on the black background. These labels are obscured by the resting position of the triangles. A hole can be drilled in the back for hanging somewhere.

And that's it. Now, even a five year old can demonstrate this fundamental theorem of mathematics. :)

Thursday, August 5, 2010

File Encryption on the Command Line and a Secure Text Editor

Update: I added this script to github at:
https://github.com/sevkeifert/text-utils/blob/master/svi


A while back I needed a really simple way to encrypt a password file on the command line. I wanted to be able to write quick, simple scripts, with no more than one line of code handling the file encryption part. So, in Linux or Windows (using cygwin), I used these aliases:

# for simple encryption methods, use:
#     echo "text" | encrypt
#     echo "U8askj123lkjasdflkjasdlkjargoi+/==" | decrypt

alias encrypt="openssl aes-256-cbc -a -salt"
alias decrypt="openssl aes-256-cbc -d -a -salt"


Then other quick shell scripts can easily encrypt/decrypt files as needed.

However, while this works, it was still a nuisance to actually edit and maintain the encrypted files. Here's an alternate approach that will work under Linux or Windows (using cygwin). It wraps the classic vi text editor, or any editor for that matter, in a shell script that opens and saves files in any standard encryption format (like aes 256), which can then be decrypted by other script utilities. Also, it will save an encrypted snapshot of the file every day it's updated in case you mangle the password or file.

Here's the bash script, which can be saved to /usr/local/bin/svi

#!/bin/bash
# A simple secure vi editor, for aes encrypted files
# file name passed in as argument, for example
#     svi FILENAME
# to open file as read only
#     ln -s svi sview
#     sview FILENAME
# GPL

# type of encryption
cipher=aes-256-cbc

# where temp files will be decrypted
tmp=/tmp/


# create tmp dir/file
me=`basename $0`
dir=`dirname "$tmp$1.tmp"`
mkdir -p "$dir"
touch "$tmp$1.tmp"
chmod 600 "$tmp$1.tmp"

# decrypt to tmp
if [[ -e "$1" ]]
then

 if openssl $cipher -d -a -salt -in "$1"  > "$tmp$1.tmp"
 then
   if [[ ! -s "$tmp$1.tmp" ]]
   then
     echo "WARNING: decrypted an empty file, exiting."
     exit
   fi
 else
   echo "WARNING: bad password, exiting."
   exit
 fi

fi


if [[ $me == 'sview' ]]
then
 # open file read only
 vi -n -R "$tmp$1.tmp"

else
 # open file for editing
 vi -n "$tmp$1.tmp"

 # re-encrypt after exiting vi
 if [[ -s "$tmp$1.tmp" ]]
 then
   touch "$tmp$1.aes"
   chmod 600 "$tmp$1.aes"
   while [[ ! -s "$tmp$1.aes" ]]
   do
     openssl $cipher -a -salt -in "$tmp$1.tmp" > "$tmp$1.aes"
   done

   # make backup, replace old
   if [[ -s "$tmp$1.aes" ]]
   then
     if [[ -s "$1" ]]
     then
       cp -pf "$1" $1.$(date "+%Y-%m-%d").bac
     fi
     mv -f "$tmp$1.aes" "$1"
   fi
 fi

fi

# cleanup
shred -z -u "$tmp$1.tmp"
rm -f "$tmp$1.aes"



Then for example, to open or create a new encrypted text file, use:

svi  YOUR_FILE



Disclaimer

Overall, this is a reasonably simple and secure method for protecting files, in the case a laptop is lost or stolen. The strength is that these files can be transferred across or stored on insecure media.

As for weaknesses though, this does create a decrypted copy of the file on the local computer while it is being edited. And the temp file, even if deleted (unlinked), may leave an image on the local disk. Another weak point is that insecure memory could be flushed to swap space on the hard disk.

And, as with any encryption cipher, it can be always broken with the old "wrench" method.... :)



(comic courtesy of xkcd)

Friday, July 23, 2010

Does 1=.999...?

I heard a joke about logic a while back:

One man's modus ponens is another man's modus tollens

Though, I am hesitant to tell such a joke, for fear it will injure someone through uncontrollable side-splitting laughter. I am not responsible for burst spleens! ;)

It basically means any valid chain of reasoning can be run in two directions. For example:

Here's one proof for 1 = .999...


In Axiomatic System 1


(1) Assume: 1/3 = .333...
(2) Then adding this equation to itself three times results in:

1/3 = .333...
+ 1/3 = .333...
+ 1/3 = .333...
-------------------
1 = .999...

(3) Therefore 1 = .999...

However, this result seems a bit counter-intuitive.

There is however an equally valid argument that can be constructed by running this same chain of reasoning in reverse:


In Axiomatic System 2

(1) Assume: 1/3 = .333...
(2) Then adding this equation to itself three times results in:

1/3 = .333...
+ 1/3 = .333...
+ 1/3 = .333...
-------------------
1 = .999...

(3) Therefore, 1 = .999...
(4) However, clearly this conclusion is absurd. There is one point of difference, if we define the set of real numbers by repeatedly dividing a line into ten parts. The coordinate .999... would describe the next-to-last point in the line.
(5) Therefore we can conclude that the assumption (1) was false (by reductio ad absurdum).
(6) And since 1 > .999... by one point,

1/3 > .333...

Just like that we get a completely different conclusion.


Point Schmoint

Generally both axiomatic systems coincide on calculations, literally, 99.9... % of the time. Does a difference of a point or two matter? Who would notice the error in a calculation off by such a small amount?

The practical significance will emerge in a calculation that amplifies the difference, such as:

(1 - .999...) * (the number of points in a line of length 1)

This infinite summation is an indeterminate form, roughly:

0 * oo

In the first axiomatic system we may take this product to be 0. Since if the difference is truly zero, zero times anything must be zero.

In the second axiomatic system we may take this product to be equal to 1. Since if N represents the non-finite number of points in the line, 1/N * N = 1.

Neither 0 or 1 is "the" correct answer, any more than saying Checkers is "more correct" than Othello. Different ground rules will lead to different results in different games. A chain of reasoning only establishes a connection between a set of assumptions and the conclusion. It does not establish a direction the reasoning must travel along that chain. Just as we can accept the conclusion, we may equally reject both the conclusion and at least one assumption which led to it.

We might even imagine a system in which 1=0 (mod 1, for example), and both answers are correct. :)

So overall, we may take these numbers '1/3' and '.333...' to be infinitely close together on the number line, but not exactly equal. Or we may take them to be *exactly* equal -- the exact same point. Both systems can be consistent. But whether or not they are equal really depends on what our definition of "is equal" is.

Wednesday, July 21, 2010

1=0

Based on the definitions and axioms of standard arithmetic,'1=0' is a contradiction. However, there is nothing intrinsically contradictory about stating that '1=0' in isolation of a given set of definitions/axioms.

Math is a lot like a game of checkers. Abstractly, an axiomatic system can be reduced to three things:
  • a set of symbols (definitions)
  • a set of rules for manipulating those symbols (axioms)
  • a set of derived results (theorems)
In my view, the symbols are like the game pieces, the axioms are like the rules of the game, and a theorem is one particular game that can be played. What matters is internal consistency of the game. Since if there is a contradiction in the basic assumptions of the system, we can literally derive any result from them.

For example here is an inconsistent system, where two core axioms contradict each other:

System X

(1) X is true (assumption)
(2) X is not true (assumption)
(3) If X is true, then either X is true or pigs can fly. ('or' introduction from 1)
(4) But since X is not true (from 2), pigs can fly ('or' elimination from 1, 2)
(5) Therefore since we can replace 'pigs can fly' with any other random absurdity, if a system is inconsistent, we can derive any assertion from it.

So, if 'inconsistency' means that any result can be derived, 'consistency' just means there is there is least one thing that cannot be derived from the set of assumptions. To show consistency of a simple system consisting of '1=0', it's easy to show we cannot derive '1 != 0'. Since, from '1=0', the most we may derive is '1=2, 1=3, 1=4, ...'.

Therefore '1=0' is only inconsistent if we introduce definitions/axioms that make it so.


Usefulness of 1=0

Though, my next thought is "So '1=0' may be logically consistent, but is it useless? What can you do with a number system in which 1=0?"

However, consider a larger context of use. Likewise we may not see a good use for the number "zero" in only the context of itself. But when combined with other numbers, in a larger system, zero is useful precisely because it is the trivial case. And the trivial case becomes a pivot point for solving an equation, given it's unique properties when applied to operators.

So what is simple a real world example? Suppose we have a ceiling fan or lamp, that has a cyclic switch with four settings:
  • off
  • low
  • medium
  • high
Then in this context of settings it makes sense to state that 4=0 (mod 4). Or in alternate notation system we could simply write '0=4'. Since 0 and 4 can be used interchangeably without changing the truth value of an assertion like "if you click the switch N times, the fan will turn off." This is a cyclic number system that loops back on itself.

Now we could physically replace the fan switch with one with fewer possible states (3, 2, or 1). In the smallest number of states, we have a switch that is hardwired to be either always on or off. This is a system where 1 = 0 (mod 1) best models the situation. So we could view a wire as a cyclic switch, where states 1=0. A collection of switches AND wires, therefore, reduce to a collection of only switches. Not that this is the only way to see a problem, but the benefit is that it leads to a different way to see a problem (without requiring as many different types of components).


Combining inconsistent branches into a consistent superstructure:

There can be a fork in mathematics where on one branch '1=0' is an axiom, and on another branch '1!=0' is an axiom. Both are consistent when taken individually, but they are inconsistent in relation to each other. But are these two systems truly incompatible though?

Not necessarily. Both could be combined into one axiomatic superstructure, where the "loop" property of the number line was a variable.

For example, in a system where '1=0' the numbers loop at 1. In modular arithmetic, the line loops at some finite number > 1. And for standard arithmetic, the loop could be placed somewhere past the largest number we need. Not that this is the only approach to combining the two systems, but it's just an example.

So I think the interesting results from this line of reasoning are:

1. For any statement that is derivable in one axiomatic system, there exists at least one alternate axiomatic system such that the exact opposite result is derivable. Since at least, we may take that opposite result to be the starting axiom of the new system.

2. In general, any two incompatible axiomatic systems can be brought together under one super set of axioms, by simply adding one (or more) variables to axioms that switch results down one branch or the other. This is similar to re-factoring in software engineering, where different cases can always be brought together within the same handler, simply by introducing more variables.

3. The difference between "internal inconsistency of one system" and "external inconsistency between two systems" is arbitrary. Any subset of assumptions may be considered to be an independent axiomatic system. So, consistency problems within a single given system can likewise be resolved by the addition of (at least) one new variable. For example, considering "System X" again, we can introduce a new variable Y that easily resolves the contradiction:

System X+Y

(1') X is true if Y (assumption)
(2') X is not true if not Y (assumption)
(3') it's not possible to derive 'pigs can fly'

So rather than seeing two complete systems or axioms as inconsistent, we may see both as special cases of a larger framework of axioms.

The pros/cons of adding new variables to core axioms is that they increase the resolution of detail, but decrease the simplicity. Really, the practical problem in designing an axiomatic system isn't so much in getting the system to be consistent, but in preventing it from becoming too complex and unwieldy to use.

Sunday, July 18, 2010

Paradox: I'm lying, and that's a promise

I remember years ago, on the old "Star Trek" series, Spock told a robot that he was lying, and it made the robot's head explode.

That paradox fascinated me, because it is such a simple paradox, but it is tricky to analyze. It cleverly sets all the logic about itself in a tight loop. I wondered:

  • Does the statement have a truth value?
  • Is the statement valid?
  • If something is screwy with the statement, what exactly is it?
After giving it some thought, my analysis of this type of paradox was:

Consider:

(1) This sentence is false.

(2) Suppose the statement at (1) is either true or false. The analysis will explore the two branches independently.
(3.a) First assume it is true. Then from (1), we can easily derive that the statement is false.
(3.b) Now assume it is false. No further deduction can be made using (1) within this branch. Since any attempt to prove the truth of (1) would also be relying on (1) as an assumption, which we just assumed to be false.
(4) Hence, either way, the reasoning terminates on the conclusion that the statement at (1) is false.

What's curious is the logic stemming from the paradox is not really symmetrical.

In deriving the falsity of the statement (1), we have a valid line of reasoning (reductio ad absurdum). However, attempting to derive the opposite result -- that the statement is is true -- is invalid (circular reasoning). Using (1) in any manner to prove that (1) is true assumes the conclusion. And if the statement is false, we don't know that the mechanism that would allow a desired inference would even be supported, as there's more than one way a statement can be false.

So then what exactly is screwy with the statement? The problem is simple, but to model this more precisely, I will need better notation.

Define a set of distinct truth values {0, 1, null}.

As an analogy, "0" will function like "false", "1" will function like "true," and just to be safe "null" is anything else that doesn't fit in the previous two categories, for example, an incomplete thought, or gibberish. The value "null" value will propagate to anything it touches.

But how would I determine if a statement would map to one of these values?
What does it means for a mathematical statement to be "true?"

For a simple example, if I defined numbers like:

Let 1 be a primitive
Let + be an operation
Let 2 = 1+1
Let 3 = 1 + 1 + 1
Let 4 = 1 + 1 + 1 + 1
...

Then a statement like 2+2=4 is "true" since it's derivable from the basic definitions/axioms:

4 = 1+1+1+1 = (1+1) + (1+1) = 2+2

Generally, in math, if "a statement is true" it just means that it's derivable from the given set of definitions/axioms. So more precisely:

Define a function truth_value(P) that maps an assertion P to its truth value such that:
The return value is 1, if P is derivable from the given set of definitions/axioms.
The return value is 0, if a contradiction (like 1=0) can be derived from P and the given set of definitions/axioms.
The return value is null otherwise.

Also the statement at (1) contains a semantic structure like "P is false," which operates like an inverter on the truth value. For example if P is true, "P is false" is false. If P is false, "P is false" is true. This function essentially just flips the truth value of P. To model this more precisely:

Define a truth function inverter(x) such that:
The return value is 1, if x = 0.
The return value is 0, if x = 1.

Note: if the number is a value 1 or 0, the function is just:

inverter(x) = 1 - x.

Now the statement at (1) is only talking about truth values. And it's asserting (at least) two different things. If we let the variable S refer to the statement at (1), the assertions are:

The statement is false:

truth_value(S) = 0

and a self-reference roughly like "S = 'S is false'" sets up an identity statement about the truth values:

truth_value(S) = inverter(truth_value(S))

I can extract more things from the statement (like more complex combinations of the above two identities), but ultimately, in exploding a statement into a package of simpler assertions, all these things would be AND'ed together to form the complete thought.

But I'm only interested in showing the falsity of the entire package. It's sufficient that if any one of these components is false, any larger AND statement containing these two assertions will also be false.

Now simplifying, if we let:

x = truth_value(S)

Then what is asserted at the bottom of the paradox is a set of linear equations:

x = 0
x = 1 - x
...

The "solution" implies that 1 = 0. By definition, this is a contradiction, so we can say the entire the complete package of assertions in (1) is false (in a system of binary truth values). The self-reference implicitly sets up an impossible set of constraints, so the reference is exactly what is false.

The interesting morals of the story, I think, are:

Generally a reference can bear a truth value. For example, suppose a wrecking crew shows up, and someone asks "Hey boss, where's the house we are supposed to demolish?" If the boss man points to the wrong house and says "That," it's precisely the reference that is false.

Self-references are fine, but they are assertions, as they implicitly set up an identity relation. Those assertions can be incorrect just like anything else. But in pointing a reference back to the statement containing the reference, the self-reference is effectively camouflaged so as to hardly look like an assertion at all.

Circular reasoning is a tricky issue to diagnose, and any kind of recursive definition will naturally lend itself to this problem.

Atari 2600 games

The Atari emulator called "Stella" brings the old Atari 2600 games back to life. Honestly these games are sometimes terrible by modern standards. :) Though, they do have an interesting primitive appeal, and I wanted screen shots of them. Maybe I will make them into a desktop background.

I think my favorite was H.E.R.O, where you could explore caverns (across multiple screens) and rescue trapped minors.

Here are more or less all the games I purchased back in the day...