๐Ÿ“ฆ sleepyfran / duets

๐Ÿ“„ Map.fs ยท 138 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138[<AutoOpen>]
module rec Duets.Cli.Components.Map

open Duets.Agents
open Duets.Cli.Text
open Duets.Cli.Text.World
open Duets.Common
open Duets.Entities
open Duets.Simulation

type private PlaceSpecialProperty =
    | Rented
    | ConcertScheduled

let private placesWithSpecialProperties state =
    let band = Queries.Bands.currentBand state
    let currentDate = Queries.Calendar.today state

    let scheduledConcert =
        Queries.Concerts.scheduleForDay state band.Id currentDate
        |> Option.map (fun scheduledConcert ->
            let concert = Concert.fromScheduled scheduledConcert

            [ concert.VenueId, PlaceSpecialProperty.ConcertScheduled ]
            |> Map.ofList)
        |> Option.defaultValue Map.empty

    let rentedPlaces =
        Queries.Rentals.all state
        |> List.collect (fun rental ->
            let _, placeId = rental.Coords
            [ placeId, PlaceSpecialProperty.Rented ])
        |> Map.ofList

    Map.merge rentedPlaces scheduledConcert


let private placeWithInfo (city: City) (place: Place) specialProperties =
    let state = State.get ()

    let currentlyOpen = Queries.World.placeCurrentlyOpen' state place

    let placeSpecialProperties = specialProperties |> Map.tryFind place.Id

    let zone = Queries.World.zoneInCityById city.Id place.ZoneId
    let zonePrefix = $"{zone.Name |> Styles.place},"

    let placeDetails =
        match placeSpecialProperties with
        | Some Rented ->
            $"""{zonePrefix} {place.Name} ({"Rented" |> Styles.highlight})"""
        | Some ConcertScheduled ->
            $"""{zonePrefix} {place.Name} ({"Concert scheduled" |> Styles.highlight})"""
        | None -> $"{zonePrefix} {place.Name}"

    World.placeNameWithOpeningInfo placeDetails currentlyOpen

let private showPlaceChoice city placesInCity places =
    let specialProperties = placesWithSpecialProperties (State.get ())

    let selectedPlace =
        showCancellableChoicePrompt
            Command.mapChoosePlace
            Generic.back
            (fun place -> placeWithInfo city place specialProperties)
            places

    match selectedPlace with
    | Some place -> Some place
    | None -> showPlaceTypeChoice city placesInCity

let private showPlaceTypeChoice
    city
    (placesInCity: Map<PlaceTypeIndex, Place list>)
    =
    let availablePlaceTypes = placesInCity |> List.ofSeq |> List.map _.Key

    showCancellableChoicePrompt
        Command.mapChoosePlaceTypePrompt
        Generic.cancel
        World.placeTypeName
        availablePlaceTypes
    |> Option.bind (fun placeType ->
        placesInCity |> Map.find placeType |> showPlaceChoice city placesInCity)

let private changePlace idxType fn (places: Map<PlaceTypeIndex, Place list>) =
    Map.change idxType (Option.bind (fn >> Option.ofList)) places

let private filterRentedHomePlaces (currentCity: City) rentedPlaces =
    changePlace
        PlaceTypeIndex.Home
        (List.filter (fun (place: Place) ->
            rentedPlaces |> Map.containsKey (currentCity.Id, place.Id)))

let private filterStreets =
    changePlace PlaceTypeIndex.Street (List.filter (fun _ -> false))

let private sortHotels (currentCity: City) rentedPlaces =
    changePlace
        PlaceTypeIndex.Hotel
        (List.sortByDescending (fun (place: Place) ->
            rentedPlaces |> Map.containsKey (currentCity.Id, place.Id)))

let private sortConcertSpaces state =
    let band = Queries.Bands.currentBand state
    let currentDate = Queries.Calendar.today state

    let scheduledConcert =
        Queries.Concerts.scheduleForDay state band.Id currentDate

    match scheduledConcert with
    | Some scheduledConcert ->
        let concert = Concert.fromScheduled scheduledConcert

        changePlace
            PlaceTypeIndex.ConcertSpace
            (List.sortByDescending (fun (place: Place) ->
                place.Id = concert.VenueId))
    | None -> id

/// Shows a list of all the place types in the current city and, upon selecting
/// one type, shows all the places of that specific type in the current city.
/// Returns the selected place if one was chosen, or None if the user cancelled.
let showMap () =
    let state = State.get ()
    let rentedPlaces = Queries.Rentals.allAsMap state
    let currentCity = Queries.World.currentCity state

    (* Filter out homes that are not rented to not pollute the map. *)
    let allAvailablePlaces =
        Queries.World.allPlacesInCurrentCity state
        |> filterRentedHomePlaces currentCity rentedPlaces
        |> filterStreets
        |> sortHotels currentCity rentedPlaces
        |> sortConcertSpaces state

    allAvailablePlaces |> showPlaceTypeChoice currentCity