कार्यात्मक प्रोग्रामिंग की संभावनाओं से प्रेरित, विशेष रूप से एफ # में, और
एक उदाहरण के साथ देखा गया कि आप बस कुछ दर्जन लाइनें बना सकते हैं, मैंने
सबसे जटिल फ़्लैश गेम के एक सरल संस्करण को लागू करने का फैसला किया।
जल्दी से, लेकिन
मुख्य वस्तुएं
पहले हम यह निर्धारित करते हैं कि हमें किस प्रकार की वस्तुओं के साथ काम करना होगा। जाहिर है, हम खुद एक लाल वर्ग, पीले सिक्के और स्वतंत्र नीले हत्यारों के रूप में होंगे। ये सभी वर्ग इंटरफ़ेस को लागू करेंगे।
type IPaintObject = abstract Paint : Graphics -> unit abstract Recalc : float -> unit
पेंट फॉर्म पर आकर्षित होगा, और रिकाल (समय) गणना करेगा कि ऑब्जेक्ट समय पर कहां होगा।
सभी ऑब्जेक्ट एक सरणी में होंगे।
let po = new ResizeArray<IPaintObject>()
RedSquare
काम करने के लिए सबसे सरल वस्तु जिसके साथ आपको केवल इसके मौजूदा मापदंडों (स्थिति, आकार) और स्थिति (जीवित या मरना, क्योंकि यह धीरे-धीरे मर जाएगा) को जानने की आवश्यकता है।
type RedSquare(xx:int, yy:int, ww:int, hh:int, speed:int) = ... member rs.X with get() = int xCoord and set(v) = (xCoord <- v) member rs.Y with get() = int yCoord and set(v) = (yCoord <- v) member rs.W with get() = width and set(v) = (width <- v) member rs.H with get() = height and set(v) = (height <- v) member rs.Got with get() = gather // member rs.isDying with get() = (dying>0) member rs.Speed = speed
आइए ड्रा करें (मरने की प्रक्रिया को याद कर रहे हैं)।
interface IPaintObject with member obj.Paint(g) = let rect = match (dying) with | 0 -> Rectangle(x=int xCoord-width/2, y=int yCoord-height/2, width=width, height=height) ... g.FillRectangle(Brushes.Red, rect) g.DrawRectangle(new Pen(Color.Black, float32 2), rect)
कठिन हिस्सा रिकाल को लागू कर रहा है। कठिनाई नक्शे की सीमाओं से परे जाने के लिए नहीं है। लेकिन उस पर और बाद में, क्योंकि हम अभी भी नहीं जानते हैं कि स्तर कैसे निर्धारित किया जाए।
YellowCircle
सिक्के। रोटेशन की स्थिति और गति से निर्धारित करें
type YellowCircle(xx:int, yy:int, rr:int, tr:float) = ...
वर्ग के कार्यान्वयन में कुछ भी दिलचस्प नहीं है, केवल आपको यह जांचने की आवश्यकता है कि यह RedSquare के साथ है या नहीं। यह Recalc पद्धति में किया जा सकता है।
सबसे पहले, सरणी से एक लाल वर्ग खींचें
let rs = seq { for obj in po do match obj with | :? RedSquare as p -> yield p | _ -> yield! Seq.empty } |> Seq.head
एक इष्टतम तरीका नहीं है, वायुसेना की संभावनाओं को दिखाया गया है। एक सेट बनाया जाता है जिसमें ऑब्जेक्ट को जोड़ा जाता है यदि यह प्रकार का है RedSquare और कुछ नहीं - यदि कोई अन्य है। इसलिए, RedSquare केवल एक है - Seq.head को लें
इसके बाद एक सर्कल और एक वर्ग को जोड़ने की मानक समस्या आती है। यदि यह पार हो जाता है, तो सिक्के को मारें और हमारी संपत्ति में एक बिंदु जोड़ें।
if (isIntersects xx yy rr (rs.X-rs.W/2) (rs.Y-rs.H/2) (rs.W) (rs.H)) then yc.Take() rs.Add()
BlueCircle
सबसे दिलचस्प चरित्र। इसे सेट करने के लिए आपको बहुत सारे मापदंडों की आवश्यकता होती है -
type BlueCircle(xx:int, yy:int, rr:int, speed:int, segments:(int*int)[]) =
निर्देशांक, त्रिज्या, गति और खंडों का एक बंद सेट जिसके साथ यह स्थानांतरित होगा। खंडों को वैक्टर (डीएक्स, डाई) के रूप में निर्दिष्ट किया जाता है। यही है, वर्तमान स्थिति से, सर्कल पहले सेगमेंट के साथ जाएगा, फिर संबंधित दूसरे वेक्टर में बदल जाएगा और इसी तरह। अंतिम वेक्टर के बाद, यह पहले पर लौटेगा।
इस कार्यान्वयन में, एक वस्तु को एक सर्कल में स्थानांतरित करना संभव नहीं है (जब तक कि आप इसे कई, कई, बहुभुज नहीं बना सकते हैं और छोटे वैक्टर के साथ आगे बढ़ सकते हैं)।
कुछ बुनियादी वर्ग गुण
member bc.Stable with get() = (bc.TotalDist < 1e-8) // member bc.Speed with get() = float speed member bc.Dists = segments |> Array.map(fun (dx, dy) -> Math.Sqrt(float(dx*dx+dy*dy))) // member bc.TotalDist = bc.Dists |> Array.sum member bc.TotalTime = bc.TotalDist/bc.Speed
हमें रिकाल का एहसास है।
यह अच्छा है कि मोडुलो भिन्नात्मक संख्या लेने की संभावना है। इसलिए, चूंकि चक्र का मार्ग चक्रीय है और इसके पारित होने का समय जानने के बाद, आप वर्तमान स्थिति निर्धारित कर सकते हैं
member bc.Recalc(tt) = // - , if (bc.Stable=false) then let mutable t1 = tt%bc.TotalTime let mutable ind = 0 X <- xx Y <- yy // - , while (ind<len-1 && t1*bc.Speed>=bc.Dists.[ind]) do X <- X + (fst segments.[ind]) Y <- Y + (snd segments.[ind]) t1 <- t1-bc.Dists.[ind]/bc.Speed ind <- ind+1 // let (dx, dy) = (((float (fst segments.[ind]))/(bc.Dists.[ind])), ((float (snd segments.[ind]))/(bc.Dists.[ind]))) X <- X + int (dx*t1*bc.Speed) Y <- Y + int (dy*t1*bc.Speed)
RedSquare के साथ चौराहे की जांच करने के लिए, हम येलुस्क्वायर को लागू करते समय उसी विधि का उपयोग करते हैं।
नक्शा
प्राकृतिक समाधान एक मैट्रिक्स के रूप में मानचित्र सेट करना था। हम निम्नलिखित संकेतन का परिचय देते हैं
-1 - निषिद्ध क्षेत्र
0 - मुफ्त सेल
> 0 - चौकियों (हरे क्षेत्रों)। उन्हें बचाया जा सकता है। अधिकतम संख्या गोल के अंत को इंगित करती है (सभी एकत्र सिक्कों की उपस्थिति में, निश्चित रूप से)।
आकार
हां, यह सब अच्छा है, लेकिन यह तय करने का समय है कि इसे क्या और कैसे निकालना है।
फॉर्म से विरासत में मिली स्मूथफार्म क्लास को परिभाषित करें और हमारे कुछ तरीकों को जोड़ें
type SmoothForm(dx:int, dy:int, _path:string) as x = inherit Form() do x.DoubleBuffered <- true ... let mutable Map = null member x.Load(_map:int[][], obj, _need) = Map <- _map po.Clear() for o in obj do po.Add o need <- _need x.Init()
x.oad स्तर को लोड करता है, नक्शे के अनुसार, वस्तुओं की एक सरणी और स्तर को पूरा करने के लिए सिक्कों की संख्या को एकत्र किया जाना चाहिए।
x। यह मुख्य रूप से प्रत्येक हरे क्षेत्र के लिए बचत बिंदुओं के निर्देशांक की गणना करने से संबंधित है।
दरअसल, यह कीस्ट्रोक्स की पेंट विधि और अवरोधन को परिभाषित करने के लिए बना हुआ है
let form = new SmoothForm(Text="F# The world hardest game", Visible=true, TopMost=true,Width=.../* */) form.Paint.Add(fun arg -> let g = arg.Graphics for i=0 to form.rows-1 do for j=0 to form.cols-1 do match (form.map.[i].[j], (i+j)%2) with // | (-1, _) -> g.FillRectangle(Brushes.DarkViolet, j*form.DX, i*form.DY, form.DX, form.DY) // | ( 0, 0) -> g.FillRectangle(Brushes.White, j*form.DX, i*form.DY, form.DX, form.DY) // | ( 0, 1) -> g.FillRectangle(Brushes.LightGray, j*form.DX, i*form.DY, form.DX, form.DY) // | ( p, _) when p>0 -> g.FillRectangle(Brushes.LightGreen, j*form.DX, i*form.DY+1, form.DX, form.DY) // if (i>0 && (form.map.[i].[j]>=0 && form.map.[i-1].[j]<0 || form.map.[i].[j]<0 && form.map.[i-1].[j]>=0)) then g.DrawLine(new Pen(Color.Black, float32 2), j*form.DX, i*form.DY, (j+1)*form.DX, i*form.DY) // if (j>0 && (form.map.[i].[j]>=0 && form.map.[i].[j-1]<0 || form.map.[i].[j]<0 && form.map.[i].[j-1]>=0)) then g.DrawLine(new Pen(Color.Black, float32 2), j*form.DX, i*form.DY, j*form.DX, (i+1)*form.DY) for obj in po do // obj.Recalc((DateTime.Now-SS).TotalSeconds) obj.Paint(g) async { do! Async.Sleep(10) // 10 form.Invalidate() } |> Async.Start )
कुंजी को रोकना, जैसा कि यह निकला, जटिल कुछ भी नहीं
form.KeyDown // |> Event.filter(fun args -> (args.KeyValue >= 37) && (args.KeyValue <= 40)) |> Event.add (fun args -> match (args.KeyCode) with | Keys.Down -> form.Down <- 1 | Keys.Left -> form.Left <- 1 | Keys.Right -> form.Right <- 1 | Keys.Up -> form.Up <- 1 )
इसी तरह फॉर्म के लिए ।KeyUp

कुछ इस तरह ...
यह फाइलों से एक स्तर को लोड करने का तरीका जानने के लिए रहता है। ऐसा करने के लिए, हम एक फ़ंक्शन लिखते हैं जो फ़ाइल पथ को एक पैरामीटर के रूप में लेता है और स्तर के मापदंडों को देता है। फाइल जाएगी
- कार्ड का आकार
- नक्शा
- ब्लू सर्किल नंबर
- उनमें से प्रत्येक के पैरामीटर
- नंबर येलो सर्किल
- उनमें से प्रत्येक के पैरामीटर
- RedSquare निर्देशांक, आयाम और गति
let LoadLevel _path = let pp = new ResizeArray<IPaintObject>() let data = File.ReadAllLines(_path) |> Array.toSeq; let L1 = data |> Seq.skip 1 |> Seq.take n |> Seq.toArray |> Array.map(fun x -> x.Split([|' '|]) |> Array.filter(fun x -> Int32.TryParse(x, ref tmp)) |> Array.map(fun x -> Int32.Parse(x))) ...
चूंकि यह फ़ंक्शन सभी वर्गों के बाद लागू किया गया है, इसलिए आपको इसके प्रतिनिधि को फॉर्म में जोड़ना होगा
type DelegateLoad = delegate of (string) -> (int[][]*ResizeArray<IPaintObject>*int) type SmoothForm(dx:int, dy:int, _path:string) as x = ... let mutable (dd:DelegateLoad) = null ... member x.LoadNext() = currLevel <- currLevel + 1 let pathToLevel = pathToFolder+"\\"+"L"+currLevel.ToString()+".txt" if (File.Exists(pathToLevel) = false) then complete <- 1 else x.Load(dd.Invoke(pathToLevel)) x.Invalidate()
(dd.Invoke) निर्दिष्ट मापदंडों के साथ एक फ़ंक्शन निष्पादित करता है।
निष्कर्ष
बेशक, यह कार्यान्वयन लचीला या इष्टतम नहीं है।
कोड और
स्तर स्वयं शोधन की स्थिति में हैं। मुझे टिप्पणियों और सुझावों को सुनकर खुशी होगी।
युपीडी। कोड + exe + 2 स्तर