ASCII TABLE:

32  SPC   64  @    96  `
33  !     65  A    97  a
34  "     66  B    98  b
35  #     67  C    99  c
36  $     68  D   100  d
37  %     69  E   101  e
38  &     70  F   102  f
39  '     71  G   103  g
40  (     72  H   104  h
41  )     73  I   105  i
42  *     74  J   106  j
43  +     75  K   107  k
44  ,     76  L   108  l
45  -     77  M   109  m
46  .     78  N   110  n
47  /     79  O   111  o
48  0     80  P   112  p
49  1     81  Q   113  q
50  2     82  R   114  r
51  3     83  S   115  s
52  4     84  T   116  t
53  5     85  U   117  u
54  6     86  V   118  v
55  7     87  W   119  w
56  8     88  X   120  x
57  9     89  Y   121  y
58  :     90  Z   122  z
59  ;     91  [   123  {
60  <     92  \   124  |
61  =     93  ]   125  }
62  >     94  ^   126  ~
63  ?     95  _

####################################

Modulo (remainder) table
for A % B

 % | 0 1 2 3 4 5 6 7 8 9 B
---+--------------------
 0 | X 0 0 0 0 0 0 0 0 0
 1 | X 0 1 1 1 1 1 1 1 1
 2 | X 0 0 2 2 2 2 2 2 2
 3 | X 0 1 0 3 3 3 3 3 3
 4 | X 0 0 1 0 4 4 4 4 4 
 5 | X 0 1 2 1 0 5 5 5 5
 6 | X 0 0 0 2 1 0 6 6 6
 7 | X 0 1 1 3 2 1 0 7 7
 8 | X 0 0 2 0 3 2 1 0 8
 9 | X 0 1 0 1 4 3 2 1 0
 A

####################################

Variables

A variable is a name to a value you
will use later.

name = 'John'
age = 15

In this example the variables are
`name` and `age` and their values
are 'John' and 15

try this:

name = input("What is your name: ")
print(name)

Here we have one variable, called
`name` that we do not actually know
the value of, until the user types
it in

Variables in python have to start
with a letter, so you can have
variable named a1 but you cant name
it 1a

####################################

Strings

A string is an ordered sequence of
characters, for example: 'Jackie' or
'abc'

In python you can declare strings
with a pair single or double quites

"aaa"
'aaa'

"abcdef"

You can access each character by its
index, starting from 0

x = "abc"
x[0] is 'a'
x[1] is 'b'
x[2] is 'c'

####################################

Integers/Floats

Integers are whole numbers
 2
 0
 -6

Floating point numbers are fractions

1.5
3.2
-18934.2

####################################

Lists

Lists are ordered sequence of
things.

a = [1,2,3]

x = 1
b = [x,1,2,]

y = 'hello'

c = [1,2,y]

And of course you can have lists in
lists in lists..
z = [1, [2,3], ['a',4,[5,6,7]]]

You can access each element by its
indexm, starting at 0

so if a = [2,4,6], then a[0] = 2,
a[1] = 4, a[2] = 6

####################################

For

For is used if you want to do
something multiple times

for i in range(0,10,1):
  print(i)

range(start, stop, step) returns a
ranged sequence starting from 0 up
to 10 (but not including 10), each
step adding 1 until it reaches 10.

x = range(10)
for i in x:
  print(x)

you can skip the start and step
parameters to the range, then its
just range(stop), so range(10) is
the same as range(0,10,1).

you can use `break` to stop the loop
from inside

####################################

While

Do something until you break out.

while True:
  print("forever")

while <condition>:
  ... run code
  if something:
      break

You can also break the while loops.

####################################

If

X = 1 == 2

if X:
  code if X is True
else:
  code if X is False

  
####################################

Functions

Functions are mini programs with a
name that you can call as many times
as you want.

Example:

# sum all elements from a list
# that are smaller than cutoff
def sum_cutoff(data,cutoff):
  r = 0
  for d in data:
    if d < cutoff:
      r += d
  return r

a = [1,2,4,2,4,2,199,1,1]
s1 = sum_cutoff(a, 10)
s2 = sum_cutoff(a, 2)

The way to declare a function is:

def func_name(param1,param2,..):
   func code
   ...
   ...
``

the function can take as many
parameters as you want.


####################################

Classes

Classes are blueprints of code that
describe what an object can do. 


class Dog:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  def bark(self):
    if self.age > 5:
      print(self.name + ' Woof!')
    else:
      print(self.name + ' oof!')

ruffles = Dog('Ruffles', 5)
ruffles.bark()

max = Dog('Ruffles', 3)
max.bark()

To make a new dog use
Dog('Ruffles',5), this calls the
__init__ function with magic
parameter self(the newlly created
dog), name and age. ruffles.bark()
will call the bark() method on the
object ruffles. Or you can make a
bark() function that takes a name
and age:

  def bark(name, age):
     if age > 5:
       print(name + 'Woof!')
     else:
       print(name + 'oof!')
  bark('Ruffles',5)

The first one is called object
oriented programming(OOP), and the
second is called procedural
programming.

####################################

Computer Memory
In our game we made up 1 memory byte
per type,

1 -> integer  
2 -> string   
3 -> range    
4 -> list     
5 -> boolean  

When you store a value in memory,
python also puts down its type. In
the real python the type information
is much bigger, but the idea is the
same.

| computer memory                |
|..........3 0 3 1......4 1 99...|
|.....1 2..^............^../.....|
|.....^....|............|.v......|
|.....|....|...2.1 97...|.1 3....|
|.....|....|...^....5 1.|........|
|.....|....|...|....^...|........|
'-----+----+---+----|---+--------'
      |    |   |    |   |
x = 2 +    |   |    |   |
r = range(3)   |    |   |
y = 'a' -------+    |   |
a = True -----------+   |
z = [3] ----------------+

Each byte in memory has an address,
similar to list's indexes, you can
just get memory[482] which will make
the processor load the value at
address 482, so you can see the list
has type 4, length 1, and first
element points to address 99, where
it has an integer with type 1 and
value 3.

####################################

Morse code:

  'a': '.-',     'b': '-...',
  'c': '-.-.',   'd': '-..',
  'e': '.',      'f': '..-.',
  'g': '--.',    'h':'....',
  'i': '..',     'j': '.---',
  'k': '-.-',    'l': '.-..',
  'm': '--',     'n': '-.',
  'o': '---',    'p': '.--.',
  'q': '--.-',   'r': '.-.',
  's': '...',    't': '-',
  'u': '..-',    'v': '...-',
  'w': '.--',    'x': '-..-',
  'y': '-.--',   'z': '--..'
  '1': '.----',  '2': '..---',
  '3': '...--',  '4': '....-',
  '5': '.....',  '6': '-....',
  '7': '--...',  '8': '---..',
  '9': '----.',  '9': '----.',
  '0': '-----',  ' ': '/',



####################################

Destructuring:

a,b,c = [1,2,3]

a is 1
b is 2
c is 3

for x,y in [[1,2],[3,4],[5,6]]:
    print(x,y)
    # x is 1, y is 2
    # x is 3, y is 4
    # x is 5, y is 6

####################################

Exceptions:


try:
    0/0
except ZeroDivisionError:
    print("noo you cant do that")

you can `try` to run some code and
expect certain exceptions so you can
handle them, as the above example

Exceptions bubble up the stack until
someone catches them (with except)
or the program will terminate

```
def x():
    raise Exception("NOOO")

def y():
    x()

def z():
    y()
    print("success")

z()
```
    
the stack looks like:
    z() -> y() -> x()

so when x raises (throws) the
exception, it will go up until the
program exits before success is
printed, because nobody catches it.

```
def x():
    raise Exception("NOOO")

def y():
    try:
        x()
    except:
        print("moving on..")

def z():
    y()
    print("success")


z()
```        

the stack looks like:
    z() -> y() -> x()

The exception is thrown at x(), but
y() caches it, so the program
continues



####################################

Binary

    00000000  0
    00000001  1
    00000010  2
    00000011  3
    00000100  4
    00000101  5
    00000110  6
    00000111  7
    00001000  8
    00001001  9
    00001010  10
    00001011  11
    00001100  12
    00001101  13
    00001110  14
    00001111  15
    00010000  16
    00010001  17
    00010010  18
    00010011  19
    00010100  20
    00010101  21
    00010110  22
    00010111  23
    00011000  24
    00011001  25
    00011010  26
    00011011  27
    00011100  28
    00011101  29
    00011110  30
    00011111  31
    00100000  32
    00100001  33
    00100010  34
    00100011  35
    00100100  36
    00100101  37
    00100110  38
    00100111  39
    00101000  40
    00101001  41
    00101010  42
    00101011  43
    00101100  44
    00101101  45
    00101110  46
    00101111  47
    00110000  48
    00110001  49
    00110010  50
    00110011  51
    00110100  52
    00110101  53
    00110110  54
    00110111  55
    00111000  56
    00111001  57
    00111010  58
    00111011  59
    00111100  60
    00111101  61
    00111110  62
    00111111  63
    01000000  64
    01000001  65
    01000010  66
    01000011  67
    01000100  68
    01000101  69
    01000110  70
    01000111  71
    01001000  72
    01001001  73
    01001010  74
    01001011  75
    01001100  76
    01001101  77
    01001110  78
    01001111  79
    01010000  80
    01010001  81
    01010010  82
    01010011  83
    01010100  84
    01010101  85
    01010110  86
    01010111  87
    01011000  88
    01011001  89
    01011010  90
    01011011  91
    01011100  92
    01011101  93
    01011110  94
    01011111  95
    01100000  96
    01100001  97
    01100010  98
    01100011  99
    01100100  100
    01100101  101
    01100110  102
    01100111  103
    01101000  104
    01101001  105
    01101010  106
    01101011  107
    01101100  108
    01101101  109
    01101110  110
    01101111  111
    01110000  112
    01110001  113
    01110010  114
    01110011  115
    01110100  116
    01110101  117
    01110110  118
    01110111  119
    01111000  120
    01111001  121
    01111010  122
    01111011  123
    01111100  124
    01111101  125
    01111110  126
    01111111  127    
    
