F# 康威生命循环修正游戏
大家好,我现在正在做一个项目,用f#创建康威生活游戏。 我的问题是,我想让游戏在一个循环中运行,当我按下开始按钮时,它会不断地改变矩阵,直到我按下停止按钮为止 为了解释我的代码,我可以说我们在F# 康威生命循环修正游戏,f#,F#,大家好,我现在正在做一个项目,用f#创建康威生活游戏。 我的问题是,我想让游戏在一个循环中运行,当我按下开始按钮时,它会不断地改变矩阵,直到我按下停止按钮为止 为了解释我的代码,我可以说我们在循环函数中开始,然后等待,直到我按下一个按钮,然后按下按钮将给出我所采取的操作类型,例如,如果我按下开始按钮,我将获得开始操作,然后在匹配中,我将调用此操作所需的函数,即启动按钮功能 我现在遇到的问题是,当我启动startbutton函数时,它会按应有的方式循环,但我在显示中没有得到任何更改。当我调试这段代
循环
函数中开始,然后等待,直到我按下一个按钮,然后按下按钮将给出我所采取的操作类型,例如,如果我按下开始按钮,我将获得开始操作,然后在匹配中,我将调用此操作所需的函数,即启动按钮
功能
我现在遇到的问题是,当我启动startbutton
函数时,它会按应有的方式循环,但我在显示中没有得到任何更改。当我调试这段代码时,我的理解是,我正在更改我发送的列表,而不是主列表,这就是为什么我没有在屏幕上看到任何东西,这可能是错误的,也可能不是主要问题,但我没有找到我看到的行为的任何其他原因。所以我正在考虑改变我现在的结构,但是我找不到一个好的方法使它工作,因为现在即使它在正确的列表上循环,我也会被困在startbutton
功能上,这意味着我的其他按钮不能像stop按钮那样工作
总之,我想要的是能够让游戏运行并且永远不会停止,直到我按下停止按钮,然后游戏应该停止,或者如果我启动游戏并按下下一步按钮而不是开始按钮,startbutton
中发生的循环应该循环一次,这相当于游戏中的一步或一次生命循环
那么,你知道我应该如何解决这个问题,或者如何改进我现有的结构,或者你有一个不需要改变结构的解决方案吗
下面的代码是我现在所拥有的,你可以用这些代码来看看游戏在GUI中是如何工作的,也许这会给你一些解决问题的方法
open System.Windows.Forms
open System.Drawing
module Gui =
let form = new Form(Width = 528, Height = 572)
let matrixlabel = Array2D.init 16 16 (fun y x -> (new Label(Width = 32, Height = 32, BackColor = Color.White,
BorderStyle = BorderStyle.FixedSingle, Left = 32*x, Top = 32*y)))
let lblList = matrixlabel |> Seq.cast<Label> |> Seq.fold (fun l n -> n :: l) []
let btnAdd0 = new Button(Text="Start", Top = 513, Height = 20, Width = 40)
let btnAdd1 = new Button(Text="Stop", Top = 513, Height = 20, Width = 40, Left = 40)
let btnAdd2 = new Button(Text="Next", Top = 513, Height = 20, Width = 40, Left = 80)
let btnAdd3 = new Button(Text="Steps", Top = 513, Height = 20, Width = 40, Left = 120)
let btnlist = [btnAdd0; btnAdd1; btnAdd2; btnAdd3;]
let text = new TextBox(Text="", Top = 513, Left = 160);
for y in 0 .. 15 do
for x in 0 .. 15 do
form.Controls.AddRange [| matrixlabel.[y,x] |]
for i in btnlist do
form.Controls.AddRange [| i |]
form.Controls.AddRange [| text |]
open System.Windows.Forms
开放系统.绘图
模块Gui=
让形状=新形状(宽度=528,高度=572)
让matrixlabel=Array2D.init 16(有趣的y x->)(新标签(宽度=32,高度=32,背景色=Color.White,
BorderStyle=BorderStyle.FixedSingle,左=32*x,上=32*y)))
让lblList=matrixlabel |>Seq.cast |>Seq.fold(fun l n->n::l)[]
让btnAdd0=新按钮(Text=“开始”,顶部=513,高度=20,宽度=40)
让btnAdd1=新按钮(Text=“Stop”,顶部=513,高度=20,宽度=40,左侧=40)
让btnAdd2=新建按钮(Text=“Next”,顶部=513,高度=20,宽度=40,左侧=80)
让btnAdd3=新建按钮(Text=“Steps”,顶部=513,高度=20,宽度=40,左侧=120)
设btnlist=[btnAdd0;btnAdd1;btnAdd2;btnAdd3;]
让text=newtextbox(text=”“,Top=513,Left=160);
对于0中的y。。15做
对于0中的x。。15做
form.Controls.AddRange[|矩阵标签[y,x]|]
因为我在btnlist做什么
form.Controls.AddRange[|i |]
form.Controls.AddRange[| text |]
开放系统
打开System.Windows.Forms
开放图形用户界面
开放系统.绘图
打开FSharpx.Control.Observable
类型操作=
|开始
|停止
|下一个
|整数步
|点的Pnt
主模块=
//确保createSearchList中的所有单元格都在我们拥有的矩阵中
设inRange(s:int)(a:int)yx=
匹配
|当s=-1->false时为0
|15当s=1->false时
|->将x与
|当a=-1->false时为0
|15当a=1->false时
|_uu->true
//创建一个小列表,包含我们想要检查的细胞的所有邻居,直到下一代它是否存活
让createSearchList y x(列表:标签[,])=
[因为我在-1..1做
对于j in-1..1 do
如果((i y|j x)和&(i 0|j 0))和&(in范围(i)(j)y x)=真)
然后收益率(列表[y+i,x+j])]
//数一数活着的邻居的数量
让rec chek(列表:标签列表)计数器=
匹配列表
|head::tail when head.BackColor=Color.Black->chek tail(计数器+1)
|head::tail when head.BackColor=Color.White->chek tail计数器
|[]->计数器
//创建一个新的列表,描述下一代主列表的外观
让我们检查标签(标签:标签[,])=
标签
|>Array2D.mapi(fun e r u->match u with
|u.BackColor=Color.Black->将chek(createSearchList e r标签)0与
|当x<2 | | x>3->新标签(宽度=u.宽度,高度=u.高度,背景色=颜色.白色,
BorderStyle=u.BorderStyle,Left=u.Left,Top=u.Top)
|当x=2 | | x=3->新标签(宽度=u.宽度,高度=u.高度,背景色=颜色.黑色,
BorderStyle=u.BorderStyle,Left=u.Left,Top=u.Top)
|新标签(宽度=u.宽度,高度=u.高度,背景色=颜色.黑色,
BorderStyle=u.BorderStyle,Left=u.Left,Top=u.Top)
|u.BackColor=Color.White->将chek(createSearchList e r标签)0与
open System
open System.Windows.Forms
open Gui
open System.Drawing
open FSharpx.Control.Observable
type Action =
| Start
| Stop
| Next
| Steps of int
| Pnt of Point
module Main =
// making sure that all the cells in the createSearchList is inside the matrix we have
let inRange (s : int) (a : int) y x =
match y with
| 0 when s = -1 -> false
| 15 when s = 1 -> false
| _ -> match x with
| 0 when a = -1 -> false
| 15 when a = 1 -> false
| _ -> true
// create a small list contaning all the neighbors of the cell we want to check if it will live or not until the next gen
let createSearchList y x (list : Label [,]) =
[for i in -1..1 do
for j in -1..1 do
if (((i <> y || j <> x) && (i <> 0 || j <> 0)) && (inRange (i) (j) y x) = true)
then yield (list.[y+i, x+j])]
// count the amount of living neighbors
let rec chek (list : Label List) counter =
match list with
| head::tail when head.BackColor = Color.Black -> chek tail (counter+1)
| head::tail when head.BackColor = Color.White -> chek tail counter
| [] -> counter
// create a new list contating how our main list should look like in the next generation
let checkLabel (labels:Label [,]) =
labels
|> Array2D.mapi (fun e r u -> match u with
| u when u.BackColor = Color.Black -> match chek (createSearchList e r labels) 0 with
| x when x < 2 || x > 3 -> new Label(Width = u.Width, Height = u.Height, BackColor = Color.White,
BorderStyle = u.BorderStyle, Left = u.Left, Top = u.Top)
| x when x = 2 || x = 3 -> new Label(Width = u.Width, Height = u.Height, BackColor = Color.Black,
BorderStyle = u.BorderStyle, Left = u.Left, Top = u.Top)
| _ -> new Label(Width = u.Width, Height = u.Height, BackColor = Color.Black,
BorderStyle = u.BorderStyle, Left = u.Left, Top = u.Top)
| u when u.BackColor = Color.White -> match chek (createSearchList e r labels) 0 with
| x when x < 2 || x > 3 -> new Label(Width = u.Width, Height = u.Height, BackColor = Color.White,
BorderStyle = u.BorderStyle, Left = u.Left, Top = u.Top)
| x when x = 3 -> new Label(Width = u.Width, Height = u.Height, BackColor = Color.Black,
BorderStyle = u.BorderStyle, Left = u.Left, Top = u.Top)
| _ -> new Label(Width = u.Width, Height = u.Height, BackColor = Color.White,
BorderStyle = u.BorderStyle, Left = u.Left, Top = u.Top)
)
// changing the color of each cell in the main list to match the list we created for the next gen
let changeLabel (labels : Label [,]) (l : Label [,]) =
labels
|> Array2D.mapi (fun e r u -> match u with
| u when u.BackColor = Color.Black -> l.[e,r].BackColor <- Color.Black
| u when u.BackColor = Color.White -> l.[e,r].BackColor <- Color.White
)
let eventType action =
let timer = Timers.Timer(1000.)
match action with
Start -> timer.Start()
timer.Elapsed
//loops and every generation (loop) is 1 sec
let rec startbutton (l : Label [,]) (steps : int) (start : bool) (action) = async{
let event = Async.AwaitEvent (eventType action) |> Async.Ignore
Async.RunSynchronously event
let labels = checkLabel l
changeLabel labels l
return! startbutton labels (steps) start action
}
// the main loop that controlls every button click
let rec loop (l : Label [,]) (runType : IObservable<Action>) = async{
let! action = Async.AwaitObservable(runType)
match action with
| Pnt(location) -> let indexX = location.X/32
let indexY = location.Y/32
if l.[indexY, indexX].BackColor = Color.Black then
l.[indexY, indexX].BackColor <- Color.White
else
l.[indexY, indexX].BackColor <- Color.Black
return! loop l runType
| Stop -> return! loop l runType
| Start -> return! startbutton (l) (0) (true) (action)
| _ -> return! loop l runType
}
module GuiInterface =
//Here we define what we will be observing (clicks)
let check (element : string) =
match element with
| x when x = "Start" -> Start
| x when x = "Stop" -> Stop
| x when x = "Next" -> Next
| x when x = "Steps" -> Steps((text.Text |> int))
let rec lblClick (list : Label List) =
match list with
| [x] -> (Observable.map (fun _-> Pnt(x.Location)) (x.Click))
| head::tail -> Observable.merge (Observable.map (fun _-> Pnt(head.Location)) (head.Click)) (lblClick (tail))
| [] -> failwith "empty"
let rec btnClick (listbtn : Button List) (listlbl : Label List) =
match listbtn with
| [x] -> Observable.merge (Observable.map (fun _-> (check (x.Text))) (x.Click)) (lblClick (listlbl))
| head::tail -> Observable.merge (Observable.map (fun _-> (check (head.Text))) (head.Click)) (btnClick (tail) (listlbl))
| [] -> failwith "empty"
Async.StartImmediate (loop (matrixlabel) (GuiInterface.btnClick (btnlist) (lblList)));
Application.Run(form)