#lang racket (require net/url net/url-connect json slideshow/pict racket/draw) ;; let's try to make a pokedex in racket! ;; first step will be to write a function to query the pokeAPI ;; like so, https://pokeapi.co/api/v2/pokemon/ditto ;; once there is a function in place that returns data I will ;; have a choice to make -- do I want to always query the API, ;; do I want to cache results, or do I want to create an offline ;; repository of poke-data to search through and sometimes update? ;; API URL (define *POKE-API* "https://pokeapi.co/api/v2/") (define (get-pokemon id) "queries the api, returns a butt ton of info" (call/input-url (string->url (~a *POKE-API* "pokemon/" id)) ; ~a, like string-append but appends *either* strings or numbers to a string get-pure-port (compose string->jsexpr port->string))) ; QUESTION: could I use read-json here, instead? (define (dex-entry id) "selects the info we need from the api, builds a hash table of the data that will build the entry" (let ([poke-data (get-pokemon id)]) (define entry-data (make-hash)) (hash-set! entry-data "name" (hash-ref poke-data 'name)) (hash-set! entry-data "id" (hash-ref poke-data 'id)) (hash-set! entry-data "sprite" (hash-ref (hash-ref poke-data 'sprites) 'front_default)) (hash-set! entry-data "stats" (hash-ref poke-data 'stats)) (hash-set! entry-data "types" (hash-ref poke-data 'types)) entry-data)) (define (inspector h) "display the contents of a hash table for human eyeballs and let the hash table fall back out" (hash-map h (lambda (k v) (if (list? v) (map ; consider making this if a cond and testing for lists/hash tables to recursively dive into nested data (lambda (x) (inspector x)) v) (display (~a " key: " k "\nvalue: " v "\n=====\n"))))) h) ; (inspector (dex-entry 11)) ; (inspector (dex-entry "bulbasaur")) (define (capitalize-first-letter str) "utility to capitalize the first letter of a string" (cond [(non-empty-string? str) (define first-letter-str (substring str 0 1)) (define rest-str (substring str 1 (string-length str))) (string-append (string-upcase first-letter-str) rest-str)] [else ""])) (define (get-image id sprite-file-name) "download a sprite" (define img (hash-ref (dex-entry id) "sprite")) (define img-url (string->url img)) (define the-data (parameterize ([current-https-protocol 'secure]) (port->bytes (get-pure-port img-url)))) (define out (open-output-file sprite-file-name)) (write-bytes the-data out) (close-output-port out) (display-image sprite-file-name)) (define (display-image image) "display a sprite" (define display-image (bitmap (make-object bitmap% image))) (frame (scale display-image 1))) (define (see-pokemon id) "check to see if we've already got a sprite, if we don't, get it" (define sprite-file-name (~a "imgs/" id ".png")) (if (file-exists? sprite-file-name) (display-image sprite-file-name) (get-image id sprite-file-name))) ;; FIXME: I'd very much like to sort out how to display stats, types, and maybe the evolution chain. (define (poke id [debug #f]) "display some basic info about a pokemon given it's name or id" (define poke-data (dex-entry id)) (when debug (inspector poke-data)) (displayln (~a "name: " (capitalize-first-letter (hash-ref poke-data "name")))) (displayln (~a "id: " (hash-ref poke-data "id"))) (see-pokemon (hash-ref poke-data "name"))) ; using name here prevents duplicate files with different names, id.png v name.png (poke "zapdos") (poke 74) (poke "charizard") (poke "slowking") (poke "slowbro") (poke "slowpoke") (poke "kingler")