Elm drawing over list - elm

I am trying to draw a list of objects but cannot get it to work.
Secondary question is how do I make a "for loop" using ELM.
I have a
type Object a = { a | x:Float, y:Float, vx:Float, vy:Float }
type Car = Object {}
type Cars = [Car]
displayCar = move (car.x,car.y) (filled white (rect 30 20))
displayCars = ?????
I am trying to get somethign liek this to work
collage 100 100 [displayCar (head cars) -- does work
, displayCars cars -- does not work
]
In particular, the collage has multiple things it needs to plot:
[ filled pongGreen (rect gameWidth gameHeight)
, displayObjHouse (game.houses !! 0) -- so ugly code
, displayObjHouse (game.houses !! 1) -- so ugly code
, displayObjHouse (game.houses !! 2) -- so ugly code
, displayObjHouse (game.houses !! 3) -- so ugly code
, displayObjHouse (game.houses !! 4) -- so ugly code
, displayCars cars -- does not work
]

You are looking for the function map.
map : (a -> b) -> [a] -> [b]
This means that you can apply some function to a list of things and get back a list of the results.
You are very close with what you have here. I've filled in some blanks to help you keep making progress! Good luck!
type Object a = { a | x:Float, y:Float, vx:Float, vy:Float }
type Car = Object {}
type House = Object { residents : Int }
displayCar : Car -> Form
displayCar car = move (car.x,car.y) (filled black (rect 30 20))
displayCars : [Car] -> [Form]
displayCars cars = map displayCar cars
-- map : (a -> b) -> [a] -> [b]
-- In our particular example, we plug in our functions
-- displayCar : Car -> Form
-- since display car is the first argument to map, all a's become Car
-- and all b's become Form
-- So the type of map in this instance is ((Car -> Form) -> [Car] -> [Form]
someCars : [Car]
someCars = [ { x = 100, y = 10, vx = 0, vy = 0 }
, { x = 35, y = 100, vx = 0, vy = 0 }
, { x = 0, y = 0, vx = 0, vy = 0 }
]
someHouses : [House]
someHouses = [ { x = 20, y = -100, vx = 0, vy = 0, residents = 3 }
, { x = -20, y = -50, vx = 0, vy = 0, residents = 3 }
, { x = 160, y = -150, vx = 0, vy = 0, residents = 3 }
]
displayHouse : House -> Form
displayHouse house = move (house.x, house.y) (filled blue (rect 30 50))
main : Element
main =
let houses = map displayHouse someHouses
cars = map displayCar someCars
in collage 400 400 (houses ++ cars)

Related

I want to find the highest price since entry in pine script?

I want to find the highest price and exit when current price is lower than highest price. The code to find the highest price is copied from here. how can I make a simpler code that finds highest price since entry? I also want to close the deal if current price is lower than a specific price. Please help me.
// SETTING //
length1=input(1)
length3=input(3)
length7=input(7)
length20=input(20)
length60=input(60)
length120=input(120)
ma1= sma(close,length1)
ma3= sma(close,length3)
ma7= sma(close,length7)
ma20=sma(close,length20)
ma60=sma(close,length60)
ma120=sma(close,length120)
rsi=rsi(close,14)
// BUYING VOLUME AND SELLING VOLUME //
BV = iff( (high==low), 0, volume*(close-low)/(high-low))
SV = iff( (high==low), 0, volume*(high-close)/(high-low))
vol = iff(volume > 0, volume, 1)
dailyLength = input(title = "Daily MA length", type = input.integer, defval = 50, minval = 1, maxval = 100)
weeklyLength = input(title = "Weekly MA length", type = input.integer, defval = 10, minval = 1, maxval = 100)
//-----------------------------------------------------------
Davgvol = sma(volume, dailyLength)
Wavgvol = sma(volume, weeklyLength)
//-----------------------------------------------------------
length = input(20, minval=1)
src = input(close, title="Source")
mult = input(2.0, minval=0.001, maxval=50, title="StdDev")
mult2= input(1.5, minval=0.001, maxval=50, title="exp")
mult3= input(1.0, minval=0.001, maxval=50, title="exp1")
basis = sma(src, length)
dev = mult * stdev(src, length)
upper = basis + dev
lower = basis - dev
dev2= mult2 * stdev(src, length)
Supper= basis + dev2
Slower= basis - dev2
dev3= mult3 * stdev(src, length)
upper1= basis + dev3
lower1= basis - dev3
offset = input(0, "Offset", type = input.integer, minval = -500, maxval = 500)
plot(basis, "Basis", color=#FF6D00, offset = offset)
p1 = plot(upper, "Upper", color=#2962FF, offset = offset)
p2 = plot(lower, "Lower", color=#2962FF, offset = offset)
fill(p1, p2, title = "Background", color=color.rgb(33, 150, 243, 95))
//----------------------------------------------------
exit=(close-strategy.position_avg_price / strategy.position_avg_price*100)
bull=(low>upper and BV>SV and BV>Davgvol)
bux =(close<Supper and close>Slower and volume<Wavgvol)
bear=(close<Slower and close<lower and SV>BV and SV>Wavgvol)
hi=highest(exit,10)
// - INPUTS
ShowPivots = input(true, title="Show Pivot Points")
ShowHHLL = input(true, title="Show HH,LL,LH,HL markers on Pivots Points")
left = input(5, minval=1, title="Pivot Length Left Hand Side")
right = input(5, minval=1, title="Pivot Length Right Hand Side")
ShowSRLevels = input(true, title="Show S/R Level Extensions")
maxLvlLen = input(0, minval=0, title="Maximum S/R Level Extension Length (0 = Max)")
ShowChannel = input(false, title="Show Levels as a Fractal Chaos Channel")
//
ShowFB = input(true, title="Show Fractal Break Alert Arrows")
// Determine pivots
pvtLenL = left
pvtLenR = right
// Get High and Low Pivot Points
pvthi_ = pivothigh(high, pvtLenL, pvtLenR)
pvtlo_ = pivotlow(low, pvtLenL, pvtLenR)
// Force Pivot completion before plotting.
pvthi = pvthi_
pvtlo = pvtlo_
// ||-----------------------------------------------------------------------------------------------------||
// ||--- Higher Highs, Lower Highs, Higher Lows, Lower Lows -------------------------------------------||
valuewhen_1 = valuewhen(pvthi, high[pvtLenR], 1)
valuewhen_2 = valuewhen(pvthi, high[pvtLenR], 0)
higherhigh = na(pvthi) ? na : valuewhen_1 < valuewhen_2 ? pvthi : na
valuewhen_3 = valuewhen(pvthi, high[pvtLenR], 1)
valuewhen_4 = valuewhen(pvthi, high[pvtLenR], 0)
lowerhigh = na(pvthi) ? na : valuewhen_3 > valuewhen_4 ? pvthi : na
valuewhen_5 = valuewhen(pvtlo, low[pvtLenR], 1)
valuewhen_6 = valuewhen(pvtlo, low[pvtLenR ], 0)
higherlow = na(pvtlo) ? na : valuewhen_5 < valuewhen_6 ? pvtlo : na
valuewhen_7 = valuewhen(pvtlo, low[pvtLenR], 1)
valuewhen_8 = valuewhen(pvtlo, low[pvtLenR ], 0)
lowerlow = na(pvtlo) ? na : valuewhen_7 > valuewhen_8 ? pvtlo : na
// If selected Display the HH/LL above/below candle.
plotshape(ShowHHLL ? higherhigh : na, title='HH', style=shape.triangledown, location=location.abovebar, color=color.new(color.green,50), text="HH", offset=-pvtLenR)
plotshape(ShowHHLL ? higherlow : na, title='HL', style=shape.triangleup, location=location.belowbar, color=color.new(color.green,50), text="HL", offset=-pvtLenR)
plotshape(ShowHHLL ? lowerhigh : na, title='LH', style=shape.triangledown, location=location.abovebar, color=color.new(color.red,50), text="LH", offset=-pvtLenR)
plotshape(ShowHHLL ? lowerlow : na, title='LL', style=shape.triangleup, location=location.belowbar, color=color.new(color.red,50), text="LL", offset=-pvtLenR)
plot(ShowPivots and not ShowHHLL ? pvthi : na, title='High Pivot', style=plot.style_circles, join=false, color=color.green, offset=-pvtLenR, linewidth=3)
plot(ShowPivots and not ShowHHLL ? pvtlo : na, title='Low Pivot', style=plot.style_circles, join=false, color=color.red, offset=-pvtLenR, linewidth=3)
//Count How many candles for current Pivot Level, If new reset.
counthi = 0
countlo = 0
counthi := na(pvthi) ? nz(counthi[1]) + 1 : 0
countlo := na(pvtlo) ? nz(countlo[1]) + 1 : 0
pvthis = 0.0
pvtlos = 0.0
pvthis := na(pvthi) ? pvthis[1] : high[pvtLenR]
pvtlos := na(pvtlo) ? pvtlos[1] : low[pvtLenR]
hipc = pvthis != pvthis[1] ? na : color.new(color.red,50)
lopc = pvtlos != pvtlos[1] ? na : color.new(color.green,50)
// Show Levels if Selected
plot(ShowSRLevels and not ShowChannel and (maxLvlLen == 0 or counthi < maxLvlLen) ? pvthis : na, color=hipc, linewidth=1, offset=-pvtLenR , title="Top Levels",style=plot.style_circles)
plot(ShowSRLevels and not ShowChannel and (maxLvlLen == 0 or countlo < maxLvlLen) ? pvtlos : na, color=lopc, linewidth=1, offset=-pvtLenR , title="Bottom Levels",style=plot.style_circles)
// Show Levels as a Fractal Chaos Channel
plot(ShowSRLevels and ShowChannel ? pvthis : na, color=color.green, linewidth=1, style=plot.style_stepline, offset=0, title="Top Chaos Channel", trackprice=false)
plot(ShowSRLevels and ShowChannel ? pvtlos : na, color=color.red, linewidth=1, style=plot.style_stepline, offset=0, title="Bottom Chaos Channel", trackprice=false)
// //
float fixedHH = fixnan(higherhigh)
// add offset = -pvtLenR to move the plot to the left and match the HH points.
plot(fixedHH)
bool lowerThanHH = close < fixedHH
float closeHHDiff = abs(fixedHH - close)
if barstate.islast
label.new(bar_index, high + 3*tr, tostring(closeHHDiff), xloc.bar_index, color = color.gray, style = label.style_label_down)
// STRATEGY LONG //
if (bull and close>ma3 and ma20>ma60)
strategy.entry("Long",strategy.long,1)
if (higherhigh*0.80==close)`enter code here`
strategy.close("Long",1)
imInATrade = strategy.position size != 0
highestPriceAfterEntry = valuewhen(imInATrade, high, 0)
The code above finds the highest price after entry or when you're in a trade.

How to round and resize two rectangles and four circles creating a square shape in Kotlin?

I am learning Kotlin and I am facing a challenge here:
How can I round and resize two rectangles and four circles creating a square shape in Kotlin using canvas, until it gets a ball or a perfect square?
We have this code already:
import pt.isel.canvas.*
private fun Canvas.drawSquare(r: RoundSquare) {
erase()
val f = (r.side/2 * r.round/100f).toInt()
val pos = Position(r.center.x,r.center.y)
val square =
drawRect(pos.x-150, pos.y-100,r.side+100,r.side, r.color)
drawRect(pos.x-100, pos.y-150, r.side, r.side+100, r.color)
drawCircle(pos.x-100, pos.y-100, f, r.color)
drawCircle(pos.x+100, pos.y-100, f, r.color)
drawCircle(pos.x-100, pos.y+100, f, r.color)
drawCircle(pos.x+100, pos.y+100, f, r.color)
return square
}
fun main () {
onStart {
val cv = Canvas(600, 400, WHITE)
var roundSquare = RoundSquare(Position(300, 200), 200, 50, GREEN)
cv.drawSquare(roundSquare)
cv.drawText(10,400,"center=(${roundSquare.center.x},${roundSquare.center.y}) side=${roundSquare.side} round=${roundSquare.round}% color=0x${roundSquare.color.toString(16).padStart(6, '0').toUpperCase()}",BLACK,15)
cv.onMouseDown {
roundSquare = roundSquare.copy(center = Position(it.x, it.y))
cv.drawSquare(roundSquare)
return#onMouseDown cv.drawText(10,390,"center=(${roundSquare.center.x},${roundSquare.center.y}) side=${roundSquare.side} round=${roundSquare.round}% color=0x${roundSquare.color.toString(16).padStart(6, '0').toUpperCase()}",BLACK,15)
}
cv.onKeyPressed {
roundSquare = roundSquare.processKey(it.char)
cv.drawSquare(roundSquare)
return#onKeyPressed cv.drawText(10,400,"center=(${roundSquare.center.x},${roundSquare.center.y}) side=${roundSquare.side} round=${roundSquare.round}% color=0x${roundSquare.color.toString(16).padStart(6, '0').toUpperCase()}",BLACK,15)
}
onFinish { println("Bye") }
}
}
import pt.isel.canvas.BLACK
import pt.isel.canvas.WHITE
data class Position (val x:Int, val y:Int)
data class RoundSquare (val center:Position, val side:Int, val round:Int, val color:Int)
val RANGE_SIZE = 10..400
val ROUND = 0..100
val RANDOM_COLOR = BLACK..WHITE
fun RoundSquare.processKey(key: Char) = when {
key=='r' && round > ROUND.first -> copy(round = round - 1, side = side -1)
key=='R' && round < ROUND.last -> copy(round = round + 1, side = side + 1)
key=='s' && side > RANGE_SIZE.first -> copy(side = side - 1, round = round - 1)
key=='S' && side < RANGE_SIZE.last -> copy(side = side + 1, round = round + 1)
key == 'c' -> copy(color = RANDOM_COLOR.random())
else -> this
}
But it doesn't give me the output I need. This is the output:
Which can be resized till it shows a perfect ball or perfect square, by resizing sides and rounding circles.
If anyone could help me, I would really appreciate it.
Thanks in advance,
Let rounded shape center is (cx, cy), halfsize is hs.
Left x-coordinate is lx = cx - hs
Top y-coordinate is ty = cy - hs
Right x-coordinate is rx = cx + hs
Bottom y-coordinate is by = cy + hs
We want to change parameter t from 0 to 1 (or from 0 to 100%) to make needed roundness.
Circles radius is (round to integer if needed)
R = hs * t
Circle centers coordinates:
lx + R, ty + R
rx - R, ty + R
rx - R, by - R
lx + R, by - R
Two corners of rectangles:
(lx + R, ty) - (rx - R, by)
(lx, ty + R) - (rx, by - R)

Update model at each iteration of list

Update, see below
I'm currently learning Elm and this is my first functional language to begin with.
I have a List Resource and a List Converter in my model. A Converter takes in a List Resource and outputs another List Resource. The conversion only happens when there are enough inputs. I then want to iterate through each Converter and reduce/increase the number of resources.
I couldn't wrap my head around a solution. I have a feeling that List.foldr is what I should use but I'm not sure. How should I update the model so that it will get updated each iteration?
This is what I have so far:
type alias Resource =
{ resourcetype : ResourceType
, quantity: Int
}
type ResourceType
= Energy
| Water
| Metal
type alias Converter =
{ convertertype : ConverterType
, intakes : List (ResourceType, Int)
, outputs: List (ResourceType, Int)
}
type ConverterType
= SolarCollector
| Humidifer
| MetalCollector
type alias Model =
{ resources : List Resource
, converters : List Converter
}
type Msg
= Tick Time
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick time ->
(updateGame model, Cmd.none)
convert : List Resource -> Converter -> List Resource
convert resources converter =
let
intakes = converter.intakes
outputs = converter.outputs
-- TODO, remove intakes from resources and add outputs to resources
in
resources
updateGame : Model -> Model
updateGame model =
-- TODO, call convert on each converter and update model
model
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every second Tick
Examples:
Resources won't deplete:
--Initial converters
[ SolarCollector [] [Energy 2]
, MetalCollector [Energy 1] [Metal 1]
]
--Initial resources
[ Energy 10, Metal 0]
--After 5 ticks
[ Energy 15, Metal 5]
Resources will deplete:
--Initial converters
[ MetalCollector [Energy 2] [Metal 1]
, SolarCollector [] [Energy 1]
]
--Initial resources
[ Energy 4, Metal 0]
--Tick 1
[ Energy 3, Metal 1] -- MetalCollector uses 2 Energy to get 1 Metal, SolarCollector will generate 1 Energy
--Tick 2
[ Energy 2, Metal 2] -- MC -2E,+1M; SC +1E
--Tick 3
[ Energy 1, Metal 3] -- MC -2E,+1M; SC +1E
--Tick 4
[ Energy 2, Metal 3] -- SC +1E
-- Notice how this tick the MetalCollector didn't run because of list order.
--Tick 5
[ Energy 1, Metal 4] -- MC -2E,+1M; SC +1E
Update:
I got it working! The order of the Converters matters now and it takes the right amount of resources at each tick. Here's the final, working code, thank you for helping me! Here you can try it out: https://ellie-app.com/RB3YsxwbGja1/0
module Main exposing (..)
import Html exposing (Html, div, text, program, ul, li)
import Time exposing (Time, second)
type alias Resources =
{ energy : Float
, water : Float
, metal : Float
}
resourcesToList : Resources -> List Float
resourcesToList resources =
[resources.energy, resources.water, resources.metal]
noResource : Resources
noResource = Resources 0 0 0
energyResource : Float -> Resources
energyResource energy =
{ noResource | energy = energy }
waterResource : Float -> Resources
waterResource water =
{ noResource | water = water }
metalResource : Float -> Resources
metalResource metal =
{ noResource | metal = metal }
type alias Converter =
{ convertertype : ConverterType
, quantity : Int
, intakes : Resources
, outputs: Resources
}
type ConverterType
= SolarCollector
| Humidifer
| MetalCollector
initialResources : Resources
initialResources =
{ noResource | energy = 10}
initialConverters : List Converter
initialConverters =
[ { convertertype = MetalCollector
, quantity = 2
, intakes = energyResource 1
, outputs = metalResource 1
}
, { convertertype = SolarCollector
, quantity = 2
, intakes = noResource
, outputs = energyResource 1
}
, { convertertype = Humidifer
, quantity = 1
, intakes = energyResource 1
, outputs = waterResource 1
}
]
convert : Converter -> Resources -> Resources
convert converter resources =
let
activatedQuantity =
toFloat (getActiveConverterQuantity converter resources)
getActiveConverterQuantity : Converter -> Resources -> Int
getActiveConverterQuantity converter resources =
let
resourcesList = resourcesToList resources
intakesList = resourcesToList converter.intakes
finalList =
List.map2 (,) resourcesList intakesList
|> List.filter (\(r,i) -> i > 0)
|> List.map (\(r,i) -> floor (r/i))
in
case List.maximum finalList of
Just q ->
min q converter.quantity
Nothing ->
converter.quantity
subtractIntakes : Converter -> Resources -> Resources
subtractIntakes converter resources =
{ resources
| energy = resources.energy - activatedQuantity * converter.intakes.energy
, water = resources.water - activatedQuantity * converter.intakes.water
, metal = resources.metal - activatedQuantity * converter.intakes.metal
}
addOutputs : Converter -> Resources -> Resources
addOutputs converter resources =
{ resources
| energy = resources.energy + activatedQuantity * converter.outputs.energy
, water = resources.water + activatedQuantity * converter.outputs.water
, metal = resources.metal + activatedQuantity * converter.outputs.metal
}
in
resources
|> subtractIntakes converter
|> addOutputs converter
-- MODEL
type alias Model =
{ resources : Resources
, converters : List Converter
}
init : ( Model, Cmd Msg )
init =
( { resources = initialResources
, converters = initialConverters
}
, Cmd.none
)
-- MESSAGES
type Msg
= Tick Time
-- VIEW
view : Model -> Html Msg
view model =
div []
[ div[] [text (toString model.resources)]
, div[]
[ model.converters
|> List.map
(\c -> li [] [text (toString (c.convertertype,c.quantity,c.intakes))])
|> ul []
]
]
-- UPDATE
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick time ->
(updateGame model, Cmd.none)
updateGame : Model -> Model
updateGame model =
let
newResources = model.converters |> List.foldr convert model.resources
in
{ model | resources = newResources }
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every second Tick
-- MAIN
main : Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
I'd expect your model.resources will not have to hold multiple values of one ResourceType so a Record will make things easier for that ( -> assuming resources = [energy 2, water 1, energy 2] would make no sense )
type alias Resources =
{ energy : Int
, water : Int
, metal : Int
}
type alias Model =
{ resources : Resources
, converters : List Converter
}
now you can fold the intakes of every converter and accumulate the resources
convert : Converter -> Resources -> Resources
convert {intakes, outputs} resources =
let
substractResource : (ResourceType, Int) -> Resources -> Resources
substractResource (rType, quantity) res =
case rType of
Energy ->
{ res | energy = energy - quantity }
-- TODO: same for Water and Metal
-- TODO: addResource function
hasEnoughResources newRes =
case negativeResources newRes of
-- TODO: function to check if any value of the new resources Record is negative
True -> -- return original resources if converter can not run
resources
False -> -- add outputs to res and return new recources
outputs |> List.foldr addResource newRes
in
intakes
|> List.foldr substractResource resources
|> hasEnoughResources
Bonus: combine add and substract functions to 1 function of type : (Int -> Int) -> (ResourceType, Int) -> Resources -> Resources and call it like calcResource << (+)
updateGame : Model -> Model
updateGame model =
let
newResources = model.converters |> List.foldr convert model.resources
in
{ model | resources = newResources }

Maximum in List of Records

Say I have a List of records in elm:
[ { id = 1, magnitude = 100 }
, { id = 3, magnitude = 300 }
, { id = 2, magnitude = 200 } ]
and I want to get the record with the greatest magnitude value (300). What is a good way of doing this?
The docs gives an example of using the "maximum" -method, but it uses a simple list of integers. How is it done with records?
Update based on recommendation from #robertjlooby
There is a function called maximumBy which does exactly this in elm-community/list-extra. Example:
List.Extra.maximumBy .magnitude list
Original Answer
There are a few ways to achieve this.
This first way is more concise but it involves sorting the whole list, reversing it, then taking the head.
maxOfField : (a -> comparable) -> List a -> Maybe a
maxOfField field =
List.head << List.reverse << List.sortBy field
If you want something that's more efficient and only traverses the list once, here's a more efficient version:
maxOfField : (a -> comparable) -> List a -> Maybe a
maxOfField field =
let f x acc =
case acc of
Nothing -> Just x
Just y -> if field x > field y then Just x else Just y
in List.foldr f Nothing
An example of it in use:
list =
[ { id = 1, magnitude = 100 }
, { id = 3, magnitude = 300 }
, { id = 2, magnitude = 200 } ]
main =
text <| toString <| maxOfField .magnitude list
Here is a version that uses foldl and a default record:
bigger =
let
choose x y =
if x.magnitude > y.magnitude then
x
else
y
in
List.foldl choose {id = 0, magnitude = 0} items
Sebastian's answer add an arbitrary start value which could cause a problem if all your magnitudes were negative. I would adjust to
bigger items =
case items of
[] -> []
(h :: []) -> h
(h :: tail) ->
let
choose x y =
if x.magnitude > y.magnitude then
x
else
y
in
List.foldl choose h tail

ELM: Prevent game elements from colliding / positioning over each other

I'm looking at creating a game with 2 'ships'.
Ships can move up, down, left or right. One ship is controlled by arrows, the other WASD.
However I want to prevent ships from being positioned over each other.
So in my example, the blue ship should not position itself on top of the red ship. I would expect that if the red ship moved right and hit the blue ship - neither ship would move.
Any help is appreciated.
GIST
module Game (..) where
import Graphics.Element exposing (..)
import Graphics.Collage exposing (..)
import Color exposing (red, blue, gray, green)
import Keyboard
import Window
-- ALIAS
type alias Model =
{ color : Color.Color
, isFiring : Bool
, name : String
, y : Int
, x : Int
}
-- MODEL
initialShip : String -> Model
initialShip name =
let
color =
if name == "ship1" then
red
else
blue
in
{ color = color
, isFiring = False
, name = name
, y = 0
, x = 0
}
-- POSITIONS
moveLeft : Model -> Model
moveLeft model =
{ model | x = model.x - 1 }
moveRight : Model -> Model
moveRight model =
{ model | x = model.x + 1 }
moveDown : Model -> Model
moveDown model =
{ model | y = model.y - 1 }
moveUp : Model -> Model
moveUp model =
{ model | y = model.y + 1 }
-- ACTIONS
type Action
= NoOp
| Left
| Right
| Down
| Up
-- UPDATE
update : Action -> Model -> Model
update action model =
case action of
NoOp ->
model
Left ->
moveLeft model
Right ->
moveRight model
Down ->
moveDown model
Up ->
moveUp model
-- View
drawGame : Float -> Float -> Form
drawGame w h =
rect w h
|> filled gray
drawShip : Float -> Model -> Form
drawShip gameHeight ship =
let
shipColor =
if ship.isFiring then green else ship.color
initialPosition =
if ship.name == "ship1" then
(toFloat (ship.x - 50))
else
(toFloat (ship.x + 50))
in
ngon 3 30
|> filled shipColor
|> rotate (degrees 90)
|> move (initialPosition, (toFloat ship.y + 50))
view : (Int, Int) -> Model -> Model -> Element
view (w, h) ship1 ship2 =
let
(w', h') = (toFloat w, toFloat h)
in
collage w h
[ drawGame w' h'
, drawShip h' ship1
, drawShip h' ship2
]
-- SIGNALS
direction : Signal { x : Int, y : Int } -> Signal Action
direction input =
let
position =
Signal.map (\{ x, y } -> { x = x, y = y }) input
delta =
Time.fps 120
toAction { x, y } =
if x < 0 then
Left
else if x > 0 then
Right
else if y < 0 then
Down
else if y > 0 then
Up
else
NoOp
actions =
Signal.map toAction position
in
Signal.sampleOn delta actions
ship1 : Signal Model
ship1 =
Signal.foldp update (initialShip "ship1") (direction Keyboard.wasd)
ship2 : Signal Model
ship2 =
Signal.foldp update (initialShip "ship2") (direction Keyboard.arrows)
-- MAIN
main : Signal Element
main =
Signal.map3 view Window.dimensions ship1 ship2
I recommend having a single model encompassing both ships, then making a data type that has both arrows and wasd, then mapping both the arrows signal and the wasd signal and merging them into a single foldp.