Sunday 29 August 2010

Flashcards on Symbian with Python

This program lets you use sets of flashcards downloaded from flashcarddb.com (or made yourself).  The cards must be lines withe front and back separated by @ signs.

'''
 Simplified Leitner.
 For use with decks from Flashcarddb.com exported with @ separators.

 Copyright 2010, Kevin Whitefoot <kwhitefoot@hotmail.com>

 License: use as you will so long as you maintain the credits.

 Language: mshell <http://www.m-shell.net>

 Method:

 See http://flashcarddb.com/leitner


Notes:

- My mobile has Python 2.2.2 so string formatting has to be done with
  the % operator not string interpolation.


'''


''' Decide if we are running in a terminal or on the mobile.  Note
that this probably only works for *nix not Windows.

Adapted from
http://www.gossamer-threads.com/lists/python/python/379465

'''
import sys
import time
import os
import os.path


def emitmobile(s):
    # Must set the cursor position because the up arrow key is
    # passed to the normal handler as well as ours so it moves the
    # cursor.
    text.set_pos(text.len())
    text.add('\n' + s)
    text.set_pos(text.len())


def emitterminal(s):
    print s


try:
    import tty, termios
   
    runningOnMobile = False
    emit = emitterminal
    def getch():
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
except:
    # '... choose Symbian stuff instead'
    runningOnMobile = True
    emit = emitmobile


CmdCorrect = 1
CmdWrong = 2
CmdQuit  = 3
CmdAllDone = 4


'''
Return true if user is correct.
'''
def getcmd():
    if runningOnMobile:
        pass
    else:
        ch = getch()
        if ord(ch) == 27: # escape
            ch = getch()
            c = ord(getch())
            if c == 65:
                return CmdCorrect
            elif c in [53, 54]:
                ch =getch()
                return CmdWrong
            else:
                return CmdWrong
        elif ch ==  'q':
            return CmdQuit
        else:
            return CmdWrong
               
       

"""

 Initialize some tables, etc.
See http:# flashcarddb.com/leitner
Deck
Number    Time until next repetition
One    None
Two    1 day
Three    3 days
Four    1 week
Five    1 month
"""
oneday = 24*60*60 #  seconds in a day
expiry = [0, 0, 1 * oneday, 3 * oneday, 7 * oneday, 30 * oneday]

lsLoaded=0
lsLoadedWithDuplicates = 1
lsZeroLength = 2
lsTooManyStates = 3
lsTooFewStates = 4

""" debug
Set verbosity higher to generate more logging output.
"""
verbosity = 0

def verbose(n, s):
    if n < verbosity :
        print s
  # end
# end


class Card:
    """ Cards are simple data structures that can be read from an open
    text file.  A card has a front and a back.  They are never
    modified by the program.
    """
    # front
    # back

    #  f is an open file
    def load(self, line):
        i = line.index("@")
        self.front = line[0:i].strip()                
        self.back = line[i+1:].strip()       


    #  f is an open file
    def save(self, file)          :
        file.writeline(front + u"@" + back)
    # end


    #  f is an open file
    def __init__(self, line):
        self.load(line)
    # end

# end


class Cards:
    """Cards is a list of Card instances in the order they were found
    in a file.  The file name is generated from the set name by
    appending .cards. 
    """
    # cards
    # setname
   
   
    def count(self):
        return len(self.cards)
    # end


    def filename(self):
        return os.path.join(pathname,self.setname) + u".cards"                
    # end


    def get(self, i):
        return self.cards[i]                
    # end


    def add(self, c):
        self.cards.append(c)                
    # end
       

    #  f is an open file
    def load(self):
        t = time.time()
        f = open(self.filename())                
        for line in f :
            c = Card(line)                
            self.add(c)                
        # end
        d = time.time()-t
        # emit(u"Cards.load: {0} cards in {1} seconds".format(len(self.cards), d))
        emit(u"Cards.load: %s cards in %s seconds" % (len(self.cards), d))
    # end


    def __init__(self, setname):
        self.setname = setname                
        self.cards = []                
        self.load()  
    # end


    #  f is an open file
    def save(self, f):
        c = null
        for c in self.cards:
            c.save(f)                
    # end
  # end
   
# end

class CardState:
    """
    The state of each card is stored in a card state object.  These
    objects are stored in a file that is updated every time a card
    changes state. To save time the new stte is simply appended to the
    end of the file.  When reading the file back later entries for the
    same card overwrite the newer ones.  The state is : written out
    again so that the file does not grow indefinitely.
    """

    # card #  index into array of cards.
    # deck #  number of the deck to which this card belongs.
    # expires #  date at which the card should be reviewed expressed as
    #  seconds from the start of the epoch.


    def getcard(self, cards):
        return cards.get(self.card)
    # end


    def __init__(self, card, deck):
        self.card = card
        self.deck = deck
        self.expires = 0
    # end


    def loadfromstr(self, s):
        a = s.split()
        self.card = int(a[0])
        self.deck = int(a[1])
        self.expires = float(a[2])
    # end


    #  f is an open file
    def load(self, file):
        s = io.readln(file)
        self.loadfromstr(s)
    # end


    #  f is an open file
    def save(self, file):
        file.write("%s %s %s\n" % (self.card, self.deck, self.expires))
    # end


    def promote(self):
        if self.deck < 5 :
            self.deck += 1                
        # end
        self.expires = time.time() + expiry[self.deck]
    # end


    def demote(self):
        self.deck = 1
        self.expires = 0
    # end

# end


"""

"""
class CardStates:

    # cards:Cards
    # cardstates #  array of CardState
    # setname
   
   
    def get(self, i):
        return self.cardstates[i]
    # end


    def card(self, i):
        cs = self.cardstates[i]
        return cs.getcard(self.cards)
  # end


    def count(self):
        return self.cards.count()
  # end


    def filename(self):
        return os.path.join(pathname,self.setname) + u".state"                
  # end


    def savestate(self):
        # emit(u"CardStates.savestate {0} states".format(len(self.cardstates)))
        emit(u"CardStates.savestate %s states" % (len(self.cardstates)))
        f = open(self.filename(),'w')
       
        for cs in self.cardstates :
            cs.save(f)
        # end
        f.close()                
    # end


    def loadstate(self):
        """
        Returns a code indicating the result.  It can happen that the
        user will update the cards file.  In such a case the states file
        will have to be updated to match.
        """
        t = time.time()
        f = open(self.filename(), 'r')
        cr = 0 #  count read
        cu = 0 #  count used
        lc = self.cards.count()
        # Initialize array of cardstates to empty .
        for i in range(lc):
            self.cardstates.append(None)
        for s in f:
            cr += 1 #  count how many we read so that we can tell if
                    #  there were duplicates
            cs = CardState(0, 0)
            cs.loadfromstr(s)
            if cs.card < lc :
                #  Note that we do not just append because we want to ensure
                #  that later duplicates override the earlier ones
                if self.cardstates[cs.card] == None :
                    cu += 1
                # end
                #  overwrite even if was not null.
                self.cardstates[cs.card] = cs
            else:
                #  do nothing, discard state for non-existent card
                pass
                # end
                # end
            if s == None:
                break
        f.close #  want to open it for writing again.
        # emit(u"CardStates load: {0} states in {1} seconds".format(cr, time.time()-t))
        # emit(u"               : {0} state used".format(cu))
        emit(u"CardStates load: %s states in %s seconds" % (cr, time.time()-t))
        emit(u"               : %s state used" % (cu))
        if cu < lc :
            #  some cards do not have a state.  This happens when the user
            #  overwrites the cards file with a new longer (more lines)
            #  file.
            return lsTooFewStates
        elif cr != cu :
            #  some cards had duplicate state records this is not a bug, it
            #  happens because when we write a new state record we do just
            #  that instead of rewriting the whole state file.  When we start
            #  the program we remove the duplicates and rewrite the file.
            return lsLoadedWithDuplicates
        else:
            return lsLoaded
        # end
        # end


    def cardstate(self, card):
        return self.cardstates[card]
    # end


    def savecardstate(self, card):
        f = open(self.filename(), "a")
        # cs:CardState = cardstates[card]
        self.cardstate(card).save(f)
        f.close()
    # end


    def promote(self, card):
        cs = self.cardstates[card]
        cs.promote()
        self.savecardstate(card)
    # end


    def demote(self, card):
        cs = self.cardstates[card]
        cs.demote()
        self.savecardstate(card)
  # end


    def initstate(self):
        #  not found, first time
        for i in range(self.cards.count()):
            cs = CardState(i, 1)
            self.cardstates.append(cs)
        # end
        self.savestate()
    # end

 
    def qfixupstates(self):
        """ Attempt to add states for cards added to the cards file
        and remove states that are no longer needed.  At this stage
        the cardstates and cards list are both ordered by card number
        so all we have to do is look for nulls in the cardstates list.
    """
        emit(u"CardStates.qfixupstates")
        for i in range(len(self.cardstates)):
            cs = self.cardstates[i]
            if cs == None :
                #  no state for this card so add one in deck 1.
                self.cardstates[i] = CardState(i, 1)
            # end
            # end
            # end


    def __init__(self, setname):
        self.setname = setname                
        self.cardstates = []
        self.cards = Cards(setname)
        if os.path.exists(self.filename()) :
            r = self.loadstate()
            if r == lsTooFewStates :
                self.qfixupstates()
                self.savestate()
            elif r == lsTooManyStates :
                self.qfixupstates()
                self.savestate()
            elif r == lsZeroLength :
                self.initstate()
                self.savestate()
            elif r == lsLoadedWithDuplicates :
                #  Save the state to remove duplicates
                self.savestate()
                pass
            elif r == lsLoaded :
                #  success, nothing to do
                pass
            else:
                emit(u"Unxpected result from loadstates: %s" % (r))
            # end
        else:
            self.initstate()
        # end
        # end


    def getnext(self, currentcard):
        now = time.time()
        c = self.count()
        cc = currentcard
       
        while cc+1 < c:
            cc += 1                
            e = self.cardstate(cc).expires
            if e <= now :
                return cc                
        return -1                
    # end

# end
   
# States
ShowingFront = 0
ShowingBack = 1

class Flash_Set:

    # setname
    # cardstates # :CardStates
   
    # currentcard
    # currentdeck
   
   
    #  Load from disk if possible.
    def __init__(self, name):
        self.setname = name                
        self.cardstates = CardStates(name)                
        self.currentcard = -1                
        self.currentdeck = 1                
        self.state = 0
        # A hack to let me overcome the fact that the cursor in a text
        # object is moved by the navigation keys
        self.timer = e32.Ao_timer()


    def getnext(self):
        c =  self.cardstates.getnext(self.currentcard)
        if c != -1 :
            return c                
        # end
       
        #  Ran off the end so check from beginning again in case any have been demoted.
        emit(u"Studied all cards due today, checking for demoted cards.")
        self.currentcard = -1
        return self.cardstates.getnext(self.currentcard)
   
    # end


    def promote(self):
        self.cardstates.promote(self.currentcard)
    # end


    def demote(self):
        self.cardstates.demote(self.currentcard)                
    # end

    def showfront(self):
        """Show the front of the current card.
        """
        self.currentcard = self.getnext()      
        if self.currentcard == -1 :
            return CmdAllDone
        self.current = self.cardstates.card(self.currentcard)
        # emit(u"{0} {1} {2}".format(self.currentdeck, self.currentcard, c.front))
        emit(u"%s %s %s" % (self.currentdeck, self.currentcard, self.current.front))
        self.state = ShowingFront


    '''
    Return true if all shown or quit.
    '''
    def shownext(self):
       
        self.showfront()
       
        #  wait for user to click a key, don't care what unless it is quit.
        k = getcmd()
        if k == CmdQuit:
            return k

        #  show the back of the card                
        # print c
        emit(self.current.back)                

        #  wait for user to respond
        k = getcmd()
        if k == CmdCorrect:
            self.promote()
        elif k == CmdWrong:
            self.demote()
        # end

        return k

    # end


    def movecursortoend(arg):
        """Move the cursor to the end of the text because it is distracting.
        """
        if runningOnMobile:
            text.set_pos(text.len())


    def handleup(self):
        # Simple hack to move the cursor to the end out of the text
        # after the underlying up arrow event has been handled by the
        # text control because I find it distracting that the up key
        # moves the cursor to the middle of the line above the end.  A
        # ten milli-second delay is enough on the N73.
        self.timer.after(0.01, self.movecursortoend)
        if self.state == ShowingBack:
            self.promote()
            return self.showfront()
        else:
            emit(u"%s" % self.current.back)                
            self.state = ShowingBack


    def handledown(self):
        if self.state == ShowingBack:
            self.demote()
            return self.showfront()
        else:
            emit(u"%s" % self.current.back)                
            self.state = ShowingBack
           
           
    def showall(self):
        self.showfront()
        while True:
            k = getcmd()
            if k == CmdWrong:
                k = self.handledown()
            elif k == CmdCorrect:
                k = self.handleup()

            if (k in [CmdQuit, CmdAllDone]):
                break
       
        if k == CmdAllDone:
            emit(u"Studied all cards + nothing left to don today.")
        else:
            emit(u"User quitting")

    # end

# end


#Define the exit function
def quit():
    app_lock.signal()


# define functions to show notifications
def up():
    f.handleup()

 
def down():
    f.handledown()


def initMobile():

    global app_lock
    app_lock = e32.Ao_lock()

    appuifw.app.exit_key_handler = quit
 
    # Create an instance of Text and set it as the application's body
    global text
    text = appuifw.Text()
    appuifw.app.body = text
 

def runMobile():

    text.add(u"Flashcards for Symbian\n")
    # Bind keys
    text.bind(key_codes.EKeyUpArrow, up)
    text.bind(key_codes.EKeyDownArrow, down)
   
    # show the first card
    f.showfront()

    # Wait for the user to request the exit
    app_lock.wait()

#  main

#ui.keys(ui.strokes) #  return keystrokes
pathname = os.path.dirname(sys.argv[0]) 
pathname = os.path.abspath(pathname)
# print pathname

if runningOnMobile:
    # Do this before loading anything because the load functions emit
    # logging information.
    import appuifw, e32, key_codes
    initMobile()

f = Flash_Set("flashcards")
if runningOnMobile:
    runMobile()
else:
    f.showall()


Posted via email from kwhitefoot's posterous

Saturday 28 August 2010

Sustainable Energy - without the hot air

I wanted to read David McKay's book on my mobile but there doesn't seem to be a reader for ePub format files for the Nokia N73.

So Free Software to the rescue in the form of Calibre by Kovid Goyal <http://calibre-ebook.com/>.  I'm using Ubuntu so all I had to do was start Synaptic and install Calibre from there.  It's not quite the latest version but it seems to work.  If you aren't using a distro with Calibre in the repository or if that sentence makes no sense to you (using Windows or Mac?) or if you simply want the latest version download it from the Calibre web site.

The web page for the book is http://www.withouthotair.com/, the ePub version is at http://www.ivor.org/withouthotair/ and the version converted from ePub to Mobi by Calibre is attached to this blog entry

Posted via email from kwhitefoot's posterous

Monday 23 August 2010

In the Beginning was the Command Line by Neal Stephenson, excerpt.

I hope Neal Stephenson doesn't mind me posting this excerpt from "In the Beginning was the Command Line".


Imagine a crossroads where four competing auto dealerships are situated. One of them (Microsoft) is much, much bigger than the others. It started out years ago selling three-speed bicycles (MS-DOS); these were not perfect, but they worked, and when they broke you could easily fix them.

There was a competing bicycle dealership next door (Apple) that one day began selling motorized vehicles--expensive but attractively styled cars with their innards hermetically sealed, so that how they worked was something of a mystery.

The big dealership responded by rushing a moped upgrade kit (the original Windows) onto the market. This was a Rube Goldberg contraption that, when bolted onto a three-speed bicycle, enabled it to keep up, just barely, with Apple-cars. The users had to wear goggles and were always picking bugs out of their teeth while Apple owners sped along in hermetically sealed comfort, sneering out the windows. But the Micro-mopeds were cheap, and easy to fix compared with the Apple-cars, and their market share waxed.

Eventually the big dealership came out with a full-fledged car: a colossal station wagon (Windows 95). It had all the aesthetic appeal of a Soviet worker housing block, it leaked oil and blew gaskets, and it was an enormous success. A little later, they also came out with a hulking off-road vehicle intended for industrial users (Windows NT) which was no more beautiful than the station wagon, and only a little more reliable.

Since then there has been a lot of noise and shouting, but little has changed. The smaller dealership continues to sell sleek Euro-styled sedans and to spend a lot of money on advertising campaigns. They have had GOING OUT OF BUSINESS! signs taped up in their windows for so long that they have gotten all yellow and curly. The big one keeps making bigger and bigger station wagons and ORVs.

On the other side of the road are two competitors that have come along more recently.

One of them (Be, Inc.) is selling fully operational Batmobiles (the BeOS). They are more beautiful and stylish even than the Euro-sedans, better designed, more technologically advanced, and at least as reliable as anything else on the market--and yet cheaper than the others.

With one exception, that is: Linux, which is right next door, and which is not a business at all. It's a bunch of RVs, yurts, tepees, and geodesic domes set up in a field and organized by consensus. The people who live there are making tanks. These are not old-fashioned, cast-iron Soviet tanks; these are more like the M1 tanks of the U.S. Army, made of space-age materials and jammed with sophisticated technology from one end to the other. But they are better than Army tanks. They've been modified in such a way that they never, ever break down, are light and maneuverable enough to use on ordinary streets, and use no more fuel than a subcompact car. These tanks are being cranked out, on the spot, at a terrific pace, and a vast number of them are lined up along the edge of the road with keys in the ignition. Anyone who wants can simply climb into one and drive it away for free.

Customers come to this crossroads in throngs, day and night. Ninety percent of them go straight to the biggest dealership and buy station wagons or off-road vehicles. They do not even look at the other dealerships.

Of the remaining ten percent, most go and buy a sleek Euro-sedan, pausing only to turn up their noses at the philistines going to buy the station wagons and ORVs. If they even notice the people on the opposite side of the road, selling the cheaper, technically superior vehicles, these customers deride them cranks and half-wits.

The Batmobile outlet sells a few vehicles to the occasional car nut who wants a second vehicle to go with his station wagon, but seems to accept, at least for now, that it's a fringe player.

The group giving away the free tanks only stays alive because it is staffed by volunteers, who are lined up at the edge of the street with bullhorns, trying to draw customers' attention to this incredible situation. A typical conversation goes something like this:

Hacker with bullhorn: "Save your money! Accept one of our free tanks! It is invulnerable, and can drive across rocks and swamps at ninety miles an hour while getting a hundred miles to the gallon!"

Prospective station wagon buyer: "I know what you say is true...but...er...I don't know how to maintain a tank!"

Bullhorn: "You don't know how to maintain a station wagon either!"

Buyer: "But this dealership has mechanics on staff. If something goes wrong with my station wagon, I can take a day off work, bring it here, and pay them to work on it while I sit in the waiting room for hours, listening to elevator music."

Bullhorn: "But if you accept one of our free tanks we will send volunteers to your house to fix it for free while you sleep!"

Buyer: "Stay away from my house, you freak!"

Bullhorn: "But..."

Buyer: "Can't you see that everyone is buying station wagons?"

Posted via email from kwhitefoot's posterous

Does Spampoison work?

Here is a link to the Spampoison page: Fight Spam! Click Here! And here is the link they suggest you add to your page: So does it work?

Posted via email from kwhitefoot's posterous

Wednesday 11 August 2010

Solar towers PS10 and PS20

Take a look at this satellite photograph from Nasa: http://earthobservatory.nasa.gov/IOTD/view.php?id=40204.

Now look at this Google map: http://maps.google.com/maps?q=seville&oe=utf-8&client=firefox-a&i...,+Andalusia,+Spain&ei=JW9iTJXAK4SXOITryc0G&ved=0CCUQ8gEwAA&ll=37.43534,-6.256714&spn=0.099776,0.17252&t=h&z=13.

You can easily identify varous features including several odd shaped fields and the housing but PS10 and PS20 seem completely absent from Google.

Odd.

 

Posted via email from kwhitefoot's posterous

Tuesday 10 August 2010

Flashcards for mshell

/* Simplified Leitner. For use with decks from Flashcarddb.com exported with @ separators. Copyright 2010, Kevin Whitefoot  License: use as you will so long as you maintain the credits. Language: mshell  Method: See http://flashcarddb.com/leitner*/ use arrayuse filesuse iouse timeuse ui/* Initialize some tables, etc.See http://flashcarddb.com/leitnerDeckNumber        Time until next repetitionOne        NoneTwo        1 dayThree        3 daysFour        1 weekFive        1 month*/oneday = 24*60*60; // seconds in a dayexpiry = [0, 0, 1, 3, 7, 30];lsLoaded=0;lsLoadedWithDuplicates = 1; lsZeroLength = 2;lsTooManyStates = 3;lsTooFewStates = 4;/* debugSet verbosity higher to generate more logging output.*/verbosity=0;function verbose(n, s);  if n < ..verbosity then    print s;  end;end;/*We don't need the precision of the UTC function so divide by seconds  in a day.  This reduces the size of the number so that it fits  in an int (32 bit) and will be correctly written by  io.format("%d"). Reducing the size also reduces the file size.  Used to generate expiry times.  */function today()  return time.utc()/..oneday;end;function debug_isnum(n)  if not isnum(n) then    ui.msg("c is not a number: " + str(n))  elsif n<0 then    ui.msg("negative: " + str(n))  end;end;/*  Cards are simple data structures that can be read from an opentext file.  A card has a front and a back.  They are never modified bythe program.*/class Card  front  back  // f is an open file  function load(file)    s = io.readln(file);    i = index(s, "@");                     front = substr(s, 0, i);                     back = substr(s, i+1);                   end  // f is an open file  function save(file);                     io.writeln(file, front + "@" + back);                   end  // f is an open file  function init(file)    load(file)  endend/*Cards is a list of Card instances in the order they were found in afile.The file name is generated from the set name by appending .cards.*/class Cards  cards  setname  function count()    return len(cards);  end  function filename()    verbose(3,"Cards.filename: " +setname);    return setname +".cards";                   end  function get(i)    return cards[i];                   end  function add(c)    append(cards, c);                   end  // f is an open file  function load()    f = io.open(filename());                     while 0 < io.avail(f) do      c:Card = Card(f);                       add(c);                     end  end  function init(setname)    this.setname = setname;                     cards = [];                     load();       verbose(3,io.format("count %d", len(cards)));                end  // f is an open file  function save(f)    c:Card = null;    for c in cards do      c.save(f);                     end  end    end/*The state of each card is stored in a card state object.  Theseobjects are stored in a file that is updated every time a card changesstate. To save time the new stte is simply appended to the end ofthe file.  When reading the file back later entries for the same card  overwrite the newer ones.  The state is then written out again  so that the file does not grow indefinitely.*/class CardState  card // index into array of cards.  deck // number of the deck to which this card belongs.  expires // date at which the card should be reviewed expressed as           // seconds from the start of the epoch.  function card(cards:Cards)    return cards.get(card);  end;  function init(card, deck)    verbose(3,"CardState.init " +str(card) + " " + str(deck));    this.card = card;    this.deck = deck;    expires = 0   end;  function loadfromstr(s)    verbose(3,"CardState.loadfromstr: " + s);    a = split(s);                     card = num(a[0]);    deck = num(a[1]);    expires = num(a[2]);  end;  // f is an open file  function load(file)    s = io.readln(file);    loadfromstr(s)  end;  // f is an open file  function save(file)    io.printf(file, "%d %d %d\n", card, deck, expires);  end;  function promote()    if deck < 5 then      deck += 1;                     end;    verbose(4,io.format(" expires %d, time.utc() %d, ..expiry[deck] %d deck %d", expires, time.utc(), ..expiry[deck], deck));    expires = time.utc() + ..expiry[deck];  end;  function demote()    deck = 1;    expires = 0;  end;end;/**/class CardStates  cards:Cards  cardstates // array of CardState  setname  function get(i)    return cardstates[i];  end;  function card(i)    cs:CardState = cardstates[i];    return cs.card(cards);  end;  function count()    return cards.count();  end;  function filename()    verbose(3,"CardStates.filename: " +setname);    return setname +".state";                   end  function savestate()    verbose(1,"CardStates.savestate");    f = io.create(filename());                     cs:CardState = null;    for cs in cardstates do      cs.save(f);    end;    io.close(f);                   end  /*  Returns a code indicating the result.  It can happen that the user  will update the cards file.  In such a case the states file will  have to be updated to match.  */  function loadstate()    verbose(1,"CardStates.loadstate");    f = io.open(filename(), false);    cr = 0; // count read    cu = 0; // count used    lc = cards.count();    cardstates = array.create(lc, null);    do      s = io.readln(f);      if s # null then        cr += 1; // count how many we read so that we can tell if there                 // were duplicates        cs:CardState = CardState(0,0);        cs.loadfromstr(s);        if cs.card < lc then          // Note that we do not just append because we want to ensure          // that later duplicates override the earlier ones          if cardstates[cs.card] = null then            cu += 1;          end;          // overwrite even if was not null.          cardstates[cs.card] = cs;        else          // do nothing, discard state for non-existent card        end;      end;    until s = null;    io.close(f); // want to open it for writing again.    verbose(3,io.format("counts %d %d %d", len(cardstates), cr, cu));    if cu < lc then      // some cards do not have a state.  This happens when the user      // overwrites the cards file with a new longer (more lines)      // file.      return ..lsTooFewStates;    elsif cr # cu then      // some cards had duplicate state records; this is not a bug, it      // happens because when we write a new state record we do just      // that instead of rewriting the whole state file.  When we start      // the program we remove the duplicates and rewrite the file.      return ..lsLoadedWithDuplicates;    else      return ..lsLoaded;    end  end;  function cardstate(card): CardState    verbose(2,io.format("CardStates.cardstate: %d %d",card, len(cardstates)));    return cardstates[card];  end;  function savecardstate(card)    verbose(2,"CardStates.savecardstate: " +str(card));    f = io.append(filename());    //cs:CardState = cardstates[card];    cardstate(card).save(f);    io.close(f);  end;  function promote(card)    verbose(3,"CardStates.promote"  +str(card));    cs:CardState = cardstates[card];    cs.promote();    savecardstate(card);  end;  function demote(card)    verbose(3,"CardStates.demote "  +str(card));    cs:CardState = cardstates[card];    cs.demote();    savecardstate(card);  end;  function initstate()    // not found, first time    verbose(1,"CardStates.initstate");    for i = 0 to cards.count()-1 do      cs = CardState(i, 1);      append(cardstates, cs);    end;    savestate();  end;  /*  Attempt to add states for cards added to the cards file and remove    states that are no longer needed.  At this stage the cardstates    and cards list are both ordered by card number so all we have to    do is look for nulls in the cardstates list.        */    function qfixupstates()    verbose(2,"CardStates.qfixupstates");    for i = 0 to len(cardstates)-1 do      cs:CardState = cardstates[i];      if cs = null then        // no state for this card so add one in deck 1.        cardstates[i] = CardState(i, 1);      end;    end;  end;  function init(setname)    verbose(2,"CardStates.init " + setname);    this.setname = setname;                     cardstates = [];    cards = Cards(setname);    if files.exists(filename()) then      r = loadstate();      verbose(3,"r: " + str(r));      if r = ..lsTooFewStates then        qfixupstates();      elsif r = ..lsTooManyStates then        qfixupstates();      elsif r = ..lsZeroLength then        initstate();      elsif r = ..lsLoadedWithDuplicates then        // Save the state to remove duplicates        savestate();      elsif r = ..lsLoaded then        // success, nothing to do      else        print "Unxpected result from loadstates: ", r;      end;    else      initstate();    end;  end  function getnext(currentcard)    verbose(2,"CardStates.getnext " + str(currentcard));    now = today();// time.utc();    c = count();    cc = currentcard;    while cc+1 < c do      cc += 1;                       e = cardstate(cc).expires;      verbose(4, io.format("Expires %d, now %d", e, now));      if e <= now then        //debug_isnum(cc);        return cc;                       end    end;    return -1;                   endend    class Flash_Set  setname  cardstates:CardStates  currentcard  currentdeck   // Load from disk if possible.  function init(name)    setname = name;                     cardstates = CardStates(name);                     currentcard = -1;                     currentdeck = 1;                   end  function getnext()    verbose(2,"Flash_set.getnext");        c =  cardstates.getnext(currentcard);    if c#-1 then      return c;                     end;    // Ran off the end so check from beginning again in case any have been demoted.    print "Studied all cards due today, checking for demoted cards.";    currentcard = -1;    return cardstates.getnext(currentcard);  end  function promote()    cardstates.promote(currentcard)  end;  function demote()    cardstates.demote(currentcard);                   end;  function shownext()    verbose(1,"Flash_set.shownext");    currentcard = getnext();           if currentcard = -1 then      return true;    end;    //debug_isnum(currentcard);    c:Card = cardstates.card(currentcard);    print currentdeck, currentcard, c.front;                     // wait for user to click a key, don't care what.    k=ui.cmd();     // show the back of the card;                     print c.back;                     // wait for user to respond    k=ui.cmd();    correct = (k = 63497); // if known    if correct then      promote()    else      demote()    end;    return false;  end;  function showall()    verbose(1, "Flash_set.showall");    do      finished = shownext();                     until finished;        print "Studied all cards, nothing left to do today.";  end;end;// mainui.keys(ui.strokes); // return keystrokesf:Flash_Set = Flash_Set("flashcards");                 f.showall()

Posted via email from kwhitefoot's posterous

Friday 6 August 2010

flashcards.m

use iouse filesuse uiclass Card  front  back  // f is an open file  function load(file)    s = io.readln(file);    i = index(s, "@");                     front = substr(s, 0, i-1);                     back = substr(s, i+1);                   end  // f is an open file  function save(file);                     io.writeln(file, front + "@" + back);                   end  // f is an open file  function init(file)    load(file)  endendclass Cards  cards  setname  function filename()    return setname +".cards";                   end  function get(i)    return cards[i];                   end  function add(c)    append(cards, c);                   end  // f is an open file  function load()    f = io.open(filename());                     while 0 < io.avail(f) do      c:Card = Card(f);                       add(c);                     end  end  function init(setname)    this.setname = setname;                     cards = [];                     load();                   end  // f is an open file  function save(f)    c:Card = null;    for c in cards do      c.save(f);                     end  end    endclass Drawers  drawers  setname  function count()    return len(drawers);  end  function filename()    return setname +".drawers";                   end  function init(setname)    this.setname = setname;                     drawers = [];                     f = io.open(filename());                     do      s = io.readln(f);                       if s # null then        append(drawers, num(s));                       end    until s = null;                   end  function save()    f = io.open(filename());                     for n in drawers do        io.writeln(f, n);                end;    io.close(f);                   end  function move(card, drawer)    drawers[card] = drawer;                   endend    class Flash_Set  setname  cards:Cards  drawers:Drawers  // Load from disk if possible.  function init(name)    setname = name;                     cards = Cards(name);                     drawers = Drawers(name);                   endendclass Show  currentcard  currentdrawer   flashset:Flash_Set    function init(set)    currentcard = -1;                     flashset = set;                     currentdrawer = 1;                   end  function getnextindrawer(drawer)    while currentcard+1 < flashset.drawers.count() do      currentcard += 1;                       d = flashset.drawers[currentcard];                       if d = drawer then        return currentcard;                       end    end;    return -1;                   end  function getnextindrawers(first, last)    for i = first to last do      c = getnextindrawer(i);                       if c#-1 then        currentdrawer = i;                         return c;                       end    end  end  function getnext()        c = getnextindrawer(currentdrawer);    if c#-1 then      return c;                     end;    return getnextindrawers(1, 5);    end  function shownext()    currentcard = getnext();                     c:Card = flashset.cards[currentcard];                     print c.front;                     // wait for user to click a key, don't care what.    k=ui.cmd();     // show the back of the card;                     print c.back;                     // wait for user to respond    k=ui.cmd();    correct = (k = 63497); // if known    if correct then      drawer = flashset.drawers[currentcard];                       if drawer < 5 then        flashset.drawers[currentcard] = drawer + 1;                       end;    else      flashset.drawers[currentcard] = 1;                     end;  end;end// mainui.keys(ui.strokes); // return keystrokesf:Flash_Set = Flash_Set("flashcards");                 s:Show = Show(f);                 while true do  s.shownext();                 end

Posted via email from kwhitefoot's posterous

Followers