;==================== begin high-scores.nls ====================================
; high-scores - Load/save/manipulate high scores in a file.
;
; By Rik Blok, 2013 <http://www.zoology.ubc.ca/~rikblok/wiki/doku.php>
;
; This is free and unencumbered software released into the public domain.
;
; Anyone is free to copy, modify, publish, use, compile, sell, or
; distribute this software, either in source code form or as a compiled
; binary, for any purpose, commercial or non-commercial, and by any
; means.
;
; Provides:
;  
;   add-high-score
;     Adds a new high score to the list and saves to file.  Asks for player name.
;   high-scores-list
;     Reports formatted list of high scores.
;   is-high-score?
;     Reports true if a score makes the list.
;   read-high-scores
;     Read the high scores from file.
;   write-high-scores
;     Save the high scores to file.
;
;
; Usage:
;
;   ; Reads high scores from _filename_ and remembers to keep _count_ highest.
;   read-high-scores _filename_ _count_
;
;   ; Reports true if _score_ should be in the high scores.
;   is-high-score? _score_
;
;   ; Adds _score_ and user-entered name to high score list.
;   add-high-score _score_ _prompt_
;
;   ; Prints a formatted high score list with specified _width_.
;   output-print high-scores-list _width_
;
; Example:
;
;   to setup
;     read-high-scores "high-scores.txt" 10
;   end
;   to go ; assume 'go' is a forever button
;     ; ...do stuff and keep score...
;     if is-game-over? ; check if game over
;     [ if is-high-score? score 
;       [ add-high-score score "You got a high score!  What is your name?" 
;       ]
;       output-print "High scores:"
;       output-print high-scores-list 25
;       stop ; stop forever button
;     ]
;   end
;
; Known limitations:
;
;   2013-09-05 - File write operations probably don't work in applet so 
;                most functionality disabled.
;   2013-09-02 - Assumes scores are non-negative integers.
; 
; Revisions:
;
;   2013-09-05 - disable functionality if running in an applet
;   2013-09-02 - initial release by Rik Blok
;-------------------------------------------------------------------------------

globals 
[ high-scores-data
  high-scores-file
  high-scores-count
]


to read-high-scores
; Read high scores from file.
; Parameters:
[ hs-file  ; high scores file
  hs-count ; number of high scores to keep
]
  if not is-string? hs-file 
  [ error "Parameter hs-file must be a filename." 
  ]
  if not is-number? hs-count or hs-count < 1
  [ error "Parameter hs-count must be an integer >= 1."
  ]
  set high-scores-file hs-file
  set high-scores-count hs-count
  set high-scores-data [ ["" 0] ]
  if file-exists? high-scores-file
  [ file-open high-scores-file
    set high-scores-data file-read
    file-close
  ]
  ; keep high scores sorted
  set high-scores-data sort-by [ last ?1 > last ?2 ] high-scores-data
end


to write-high-scores
; Write high scores to file.
  if not is-string? high-scores-file 
  [ error "You must read-high-scores before calling write-high-scores." 
  ]
  ; Replace file.
  if file-exists? high-scores-file
  [ file-delete high-scores-file
  ]
  file-open high-scores-file
  file-write high-scores-data
  file-close
end


to-report is-high-score?
; Reports whether passed score belongs on high-score list.
; Automatically reports false if running in an applet.
; Parameters:
[ hs-score ; Score to check.
]
  if netlogo-applet? [ report false ]
  if not is-list? high-scores-data
  [ error "You must read-high-scores before calling is-high-score?."
  ]
  if high-scores-count <= 0 [ set high-scores-count 10 ] ; default
  report ( length high-scores-data < high-scores-count ) or ( hs-score > last last high-scores-data )
end


to add-high-score
; Asks for name and adds to high score list.  Saves to file.
; Parameters:
[ hs-score  ; Score to be added.
  hs-prompt ; Prompt for user name.
]
  if not is-list? high-scores-data
  [ error "You must read-high-scores before calling add-high-score."
  ]
  if high-scores-count <= 0 [ set high-scores-count 10 ] ; default
  let hs-name user-input hs-prompt
  set high-scores-data lput (list hs-name hs-score ) high-scores-data
  ; keep high scores sorted
  set high-scores-data sort-by [ last ?1 > last ?2 ] high-scores-data
  ; keep highest
  set high-scores-data sublist high-scores-data 0 min (list high-scores-count length high-scores-data)
  write-high-scores
end


to-report high-scores-list
; Reports formatted high score list, restricted to specified width.
; Parameters:
[ hs-width ; column width
]
  if not is-list? high-scores-data
  [ error "You must read-high-scores before calling high-scores-list."
  ]
  ; calculate number of characters required for rank, name, and score
  let hs-rank-width ceiling log ( high-scores-count + 1 ) 10
  ; assume high-score-list already sorted so biggest score is first
  let hs-score-width ceiling log ( last first high-scores-data + 1 ) 10
  let hs-name-width hs-width - hs-rank-width - hs-score-width - 2
  if hs-name-width < 1 [ set hs-name-width 1 ]
  ; loop through list
  let hs-rank 0
  let hs-list ""
  foreach high-scores-data
  [ set hs-rank hs-rank + 1
    ; pad rank, name, and score with spaces to line up columns
    let hs-rank-str (word hs-rank)
    while [ length hs-rank-str < hs-rank-width ]
    [ set hs-rank-str word " " hs-rank-str 
    ]
    let hs-name  first ?
    while [ length hs-name < hs-name-width ] [ set hs-name word hs-name " " ]
    let hs-score ( word last ? ) 
    while [ length hs-score < hs-score-width ] [ set hs-score word " " hs-score ]
    ; add line to output
    set hs-list ( word hs-list hs-rank-str " " hs-name " " hs-score "\n" )
  ]
  report hs-list
end

;==================== end high-scores.nls ======================================
