21 Kart Oyununu Mvc, WebApi ve Jquery ile Yazıp Azure’a Publish Etme
Selamlar,
Bugünkü makalede BlacJack yani 21 kart oyununu Mvc ile, ilgili servislerini WebApi ile ve finansal transection işlemlerini CodeFirst kullanarak baştan sona kodlayıp, Azure’a publish edip oynayacağız. Amaç size algoritma mantığını aşılamak, basit bir oyunu bile yazarken nasıl zorluklar ile karşılaşacağınızı göstermektir. Ayrıca yazdığınız bir şey ile oynayıp keyif almakta ayrı bir mutluluk olacaktır. Gelin isterseniz lafı fazla uzatmadan kodlara başlıyalım.
Bir de son olarak front tarafda tamamen Jquery kullanıcam. Amaç modern javascript frameworklerinin kıymetini bilmek:)
Azure üzerinde remote debuging:
Öncelikle DB katmanında EntityFramework/CodeFirst kullanılmıştır.
DAL:
PlayLog.cs: Oynuyan oyuncuların oyun içerisindeki logları bu tabloda tutulur.
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 |
namespace DAL { using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.Spatial; [Table("PlayLog")] public partial class PlayLog { public int ID { get; set; } [StringLength(50)] public string SessionID { get; set; } public int? Cash { get; set; } public bool? Win { get; set; } public int? PutCache { get; set; } public DateTime? CreatedDate { get; set; } } } |
BlackJackContext.cs: İlgili DBContext “BlackJackContext” olarak aşağıdaki gibi tanımlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
namespace DAL { using System; using System.Data.Entity; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; public partial class BlackJackContext : DbContext { public BlackJackContext() : base("name=BlackJackContext") { } public virtual DbSet<PlayLog> PlayLog { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<PlayLog>() .Property(e => e.SessionID) .IsUnicode(false); } } } |
App.config: Yukarıdaki DB context’e ait Azure connection strings’i, aşağıdaki gibi tanımlanmıştır.
1 2 3 |
<connectionStrings> <add name="BlackJackContext" connectionString="Server=******;Database=****;User ID=*****;Password=*****;Trusted_Connection=False;Encrypt=True;" providerName="System.Data.SqlClient" /> </connectionStrings> |
Şimdi gelin hep beraber WebApi servisini tanımlayalım.
WebApiServices: Client’ın finansal anlamda hesabındaki paranın,en son işlem adımının durumunu ve zamanını sessionID bazında kaydeder. Örnek ekran çıktısı aşağıdaki gibidir.
BankController.cs: Aşağıda görüldüğü gibi:
- Client’ın SessionID’sine göre parametre alıp “PlayLog” tablosundan data çeken “Get(string session)” methodu geriye PlayLog model tipinde değer döner. Kısaca client bazında uniqe olarak tutulan alan sessionID’dir.
- Eğer ilgili sessionID’ye ait hiçbir kayıt yok ise yani siteye ilk kez girilmiş ise, default “Cash” olarak 500 para birimi atanır. Ve sayfaya ilk gelinen zaman atanır.
- “Post()” methodu “Data” tipinde parametre beklemektedir. Bu methodunda “sessionID” bazında ilgili client bulunarak, hesabında kalan para, kazanıp kazanmadığı ve en son işlem zamanı güncellenir. Kaybederse ortaya konan “putCache” miktarı client’ın parası olan “model.cache“‘den çıkarılır. Kazanma durumunda ilgili para eklenir. Güvenlik amaçlı 2 kontrol vardır. 1-) “[HttpPost]” sadece post işleminde bu method çağrılabilinir. 2-) Hem kazanma hem de kaybetme durumunda “if (_data.putCache <= model.Cash)” kontrolü yapılmakta böylece, front tarafta elle müdahale durumunda mevcut paradan daha çok para kazanılması engellenmektedir. Eğer bu gerçek bir proje olsa idi 3-) Kontrol olarak her ortaya para konulduğunda, ilgili işlem adımı servis yolu ile loglarda saklanmalı ve en son oyunun sonuçlanması durumunda, toplam kazanılan veya kaybedilen miktarın front’dan gelen data yerine bu oyun anında tutulan log değerlerine göre backend tarafında mevcut bakiye güncellenmelidir. Bu performance kaybını sağlasa da her zaman log tutmak çok önemlidir. Son olarak Post işlemlerinde “Token Based Authentication” kullanılmalıdır. Güvenlik bu makalenin esas konusu olmadığı için yazılmamıştır..
- “[EnableCors(origins: “*”, headers: “*”, methods: “*”)]” özelliği ile ilgili servisin farklı bir domainden client side taraftan çağrılması sağlanmıştır.
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 |
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using DAL; using System.Web.Http.Cors; namespace WebServices.Controllers { [EnableCors(origins: "*", headers: "*", methods: "*")] public class BankController : ApiController { // GET api/values public int Get() { return 500; } // GET api/values/5 public int? Get(string session) { using (BlackJackContext dbContext = new BlackJackContext()) { var model = dbContext.PlayLog.Where(pl => pl.SessionID == session)?.FirstOrDefault(); if (model == null) { PlayLog data = new PlayLog(); data.Cash = 500; data.CreatedDate = DateTime.Now; data.PutCache = 0; data.SessionID = session; data.Win = true; dbContext.PlayLog.Add(data); dbContext.SaveChanges(); } return model != null ? model.Cash : 500; } } [HttpPost] [EnableCors(origins: "*", headers: "*", methods: "*")] public void Post(Data _data) { using (BlackJackContext dbContext = new BlackJackContext()) { var model = dbContext.PlayLog.Where(pl => pl.SessionID == _data.session)?.FirstOrDefault(); model.PutCache = _data.putCache; model.CreatedDate = DateTime.Now; if (_data.isWin) { if (_data.putCache <= model.Cash) { model.Cash += _data.putCache; } } else { if (_data.putCache <= model.Cash) { model.Cash -= _data.putCache; } else { model.Cash = 0; } } model.Win = _data.isWin; dbContext.SaveChanges(); } } } public class Data { public int putCache { get; set; } public string session { get; set; } public bool isWin { get; set; } } } |
Bilindiği gibi bir destede 52 kağıt vardır. Herbirinin resmini ayrı ayrı yüklemek yerine “Css Sprite” kullanılarak tek bir image üzerinden belli kordinatlar verilerek istenen kart ekrana basılır. Böylece büyük bir performans sağlanmış olunur.
Ben “Css Sprite” kodlarını çıkarmak için online uygulama http://www.spritecow.com/ ‘dan faydalandım. İlgili resim upload edilip, her bir kart seçilerek, ilgili Css Sprit code’u alınabilir. Örnek ekran görüntüsü yukarıdaki gibidir.
cards.css: Aşağıda yukarıda görülen tek bir resme ait tüm kartların Sprite Css‘leri tanımlanmıştır. Top,Left kordinatları ve genişlik ile yükseklik değerleri her bir kart için tanımlanmıştır. Ayrıca isimlendirmeler de ilgili karta göre yapılmıştır.
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
.spritesA { background: url('/Images/cards.png') no-repeat; width: 74px; height: 98px; } .sprites2 { background: url('/Images/cards.png') no-repeat -73px 0px; width: 74px; height: 98px; } .sprites3 { background: url('/Images/cards.png') no-repeat -146px 0px; width: 74px; height: 98px; } .sprites4 { background: url('/Images/cards.png') no-repeat -219px 0px; width: 74px; height: 98px; } .sprites5 { background: url('/Images/cards.png') no-repeat -292px 0px; width: 74px; height: 98px; } .sprites6 { background: url('/Images/cards.png') no-repeat -365px 0px; width: 74px; height: 98px; } .sprites7 { background: url('/Images/cards.png') no-repeat -438px 0px; width: 74px; height: 98px; } .sprites8 { background: url('/Images/cards.png') no-repeat -511px 0px; width: 74px; height: 98px; } .sprites9 { background: url('/Images/cards.png') no-repeat -584px 0px; width: 74px; height: 98px; } .sprites10 { background: url('/Images/cards.png') no-repeat -657px 0px; width: 74px; height: 98px; } .spritesB { background: url('/Images/cards.png') no-repeat -730px 0px; width: 74px; height: 98px; } .spritesK { background: url('/Images/cards.png') no-repeat -803px 0px; width: 74px; height: 98px; } .spritesP { background: url('/Images/cards.png') no-repeat -876px 0px; width: 74px; height: 98px; } .spritemA { background: url('/Images/cards.png') no-repeat 0px -98px; width: 74px; height: 98px; } .spritem2 { background: url('/Images/cards.png') no-repeat -73px -98px; width: 74px; height: 98px; } .spritem3 { background: url('/Images/cards.png') no-repeat -146px -98px; width: 74px; height: 98px; } .spritem4 { background: url('/Images/cards.png') no-repeat -219px -98px; width: 74px; height: 98px; } .spritem5 { background: url('/Images/cards.png') no-repeat -292px -98px; width: 74px; height: 98px; } .spritem6 { background: url('/Images/cards.png') no-repeat -365px -98px; width: 74px; height: 98px; } .spritem7 { background: url('/Images/cards.png') no-repeat -438px -98px; width: 74px; height: 98px; } .spritem8 { background: url('/Images/cards.png') no-repeat -511px -98px; width: 74px; height: 98px; } .spritem9 { background: url('/Images/cards.png') no-repeat -584px -98px; width: 74px; height: 98px; } .spritem10 { background: url('/Images/cards.png') no-repeat -657px -98px; width: 74px; height: 98px; } .spritemB { background: url('/Images/cards.png') no-repeat -730px -98px; width: 74px; height: 98px; } .spritemK { background: url('/Images/cards.png') no-repeat -803px -98px; width: 74px; height: 98px; } .spritemP { background: url('/Images/cards.png') no-repeat -876px -98px; width: 74px; height: 98px; } .spritekA { background: url('/Images/cards.png') no-repeat 0px -196px; width: 74px; height: 98px; } .spritek2 { background: url('/Images/cards.png') no-repeat -73px -196px; width: 74px; height: 98px; } .spritek3 { background: url('/Images/cards.png') no-repeat -146px -196px; width: 74px; height: 98px; } .spritek4 { background: url('/Images/cards.png') no-repeat -219px -196px; width: 74px; height: 98px; } .spritek5 { background: url('/Images/cards.png') no-repeat -292px -196px; width: 74px; height: 98px; } .spritek6 { background: url('/Images/cards.png') no-repeat -365px -196px; width: 74px; height: 98px; } .spritek7 { background: url('/Images/cards.png') no-repeat -438px -196px; width: 74px; height: 98px; } .spritek8 { background: url('/Images/cards.png') no-repeat -511px -196px; width: 74px; height: 98px; } .spritek9 { background: url('/Images/cards.png') no-repeat -584px -196px; width: 74px; height: 98px; } .spritek10 { background: url('/Images/cards.png') no-repeat -657px -196px; width: 74px; height: 98px; } .spritekB { background: url('/Images/cards.png') no-repeat -730px -196px; width: 74px; height: 98px; } .spritekK { background: url('/Images/cards.png') no-repeat -803px -196px; width: 74px; height: 98px; } .spritekP { background: url('/Images/cards.png') no-repeat -876px -196px; width: 74px; height: 98px; } .spritekaA { background: url('/Images/cards.png') no-repeat 0px -294px; width: 74px; height: 98px; } .spriteka2 { background: url('/Images/cards.png') no-repeat -73px -294px; width: 74px; height: 98px; } .spriteka3 { background: url('/Images/cards.png') no-repeat -146px -294px; width: 74px; height: 98px; } .spriteka4 { background: url('/Images/cards.png') no-repeat -219px -294px; width: 74px; height: 98px; } .spriteka5 { background: url('/Images/cards.png') no-repeat -292px -294px; width: 74px; height: 98px; } .spriteka6 { background: url('/Images/cards.png') no-repeat -365px -294px; width: 74px; height: 98px; } .spriteka7 { background: url('/Images/cards.png') no-repeat -438px -294px; width: 74px; height: 98px; } .spriteka8 { background: url('/Images/cards.png') no-repeat -511px -294px; width: 74px; height: 98px; } .spriteka9 { background: url('/Images/cards.png') no-repeat -584px -294px; width: 74px; height: 98px; } .spriteka10 { background: url('/Images/cards.png') no-repeat -657px -294px; width: 74px; height: 98px; } .spritekaB { background: url('/Images/cards.png') no-repeat -730px -294px; width: 74px; height: 98px; } .spritekaK { background: url('/Images/cards.png') no-repeat -803px -294px; width: 74px; height: 98px; } .spritekaP { background: url('/Images/cards.png') no-repeat -876px -294px; width: 74px; height: 98px; } |
Gelin şimdi isterseniz bilmiyenler için 21 oyunun kurallarını öğrenmeye:
Bu oyunda bir canlı oyuncu bir de bilgisayar olmak üzere 2 player bulunacaktır. Kısaca kişi bilgisayar karşı oynayacaktır.
- Oyuna girmek için ortaya sanal para konulması gerekmektedir. Para fisler dediğimiz 1-5-10-25-50 ve 100lük ler olmak üzere 6 farklı şekilde seçilebilir.
- Ortaya para konulabilmesi için yeterli bakiyenin olması gerekmektedir. Başlangıçta herkesin 500tl si olmaktadır. Para 0 landıktan sonra oyuna session timeout olana kadar girilemez.
- İlgili para ortaya konulduktan sonra “Oyuna Gir” buttonuna tıklanır. Ve kart çekme işlemine başlanır.
- Player, yani oyuna katılan kişiye random 2 kart verilir. Ve toplamı ekrana yazılır.
- Computer’e de yani masaya da 1 kart verilir. O da ekrana yazılır.
- Amaç 21’e en yakın sayıya ulaşmaktır. 21 geçilirse oyun kaybedilir.
- 21’e gelene kadar 2 kartdan sonra “Yeni Kart Çek” buttonuna basılarak istenildiği kadar kart çekilebilir.
- Daha fazla kart çekilmek istenmez ise “Kal” buttonuna tıklanır. Bu sefer masa palyer’ı geçene kadar kart çekmeye başlar. 21’i aşar ise player kazanır.
- Bazen de aynı score’a gelinip berabere kalınabilir.
- Son olarak
“A” kartı özel bir karttır. Duruma göre 1 veya 11 olarak sayılabilir.21’i geçmiyor ise 11 geçiyor ise 1 score olarak sayılır.
İşte oyunun bütün kuralları bu kadar. Şimdi buna göre öncelikle front tarafı yazmaya başlıyalım. Aşağıdaki resimde index sayfasında ilgili yerlere karşılık gelen html kodlar gözükmektedir.
Index.cshtml:
- Aşağıda görüldüğü gibi kartları çekeceğimiz kısım “cardContainer” id’li div içerisine. Pc’nin kartlarının dizileceği kısım “cardContainer2” divinin altına konmaktadır.
- “firstcolumn” altına “enterbutton” id’li div’in altına, “Oyuna Gir” ve “İptal” buttonları konulmuştur. Tıklama durumunda yapılacak ilşlemler ileride tanımlanacaktır.
- “newCardButton” id’li divde “Yeni Kart Çek” ve “Kal” buttonlarının konulduğu yerdir.
- “moneycontainer” para seçmek amacı ile fişlerin konulduğu alandır (“divToken“). Herbir fişin value değeri sayısal olarak atanmıştır. Ayrıca ortaya konan paranın ve kasadaki paranın gösterildiği table alanıdır.
- Bunların altında ortaya konan resim bulunmaktadır.
- Enson satırda masnın Score’unun gösterildiği kısımdır.
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 |
<body style="background-image: url('../Images/bg.jpg');"> <input type="hidden" id="hdnSession" value="@ViewBag.Session" /> <table id="viewporttable"> <tr> <td class="firstcolumn"> <div id="cardContainer"> <img src="~/Images/dec.jpg" width="74" height="98" value="0" /> </div> </td> <td class="column"></td> <td class="column"> <div id="cardContainer2"> <img src="~/Images/dec.jpg" width="74" height="98" value="0" /> </div> </td> </tr> <tr> <td colspan="3"><div style="position:relative; min-height:20px;"><hr style="position:absolute; width:100vw;" /></div></td> </tr> <tr> <td class="firstcolumn"> <div> <div id="enterButton"> <input type="button" onclick="GetNewCard(1)" value="Oyuna Gir" disabled="disabled" id="enter"> <input type="button" onclick="Cancel()" value="İptal" id="cancel"> </div> <div id="newCardButton" style="display:none"> <input type="button" onclick="GetNewCard(3)" value="Yeni Kart Çek" id="newCard"> <input type="button" onclick="Stay()" value="Kal" id="stay"> </div> <table id="moneycontainer"> <tr> <td colspan="2" style="padding-top:20px;"> <div style="color:red">Player 1 Score:</div> <input type="text" readonly id="score" value="0" style="font-size:100px; color:red; width:120px" /> </td> </tr> <tr> <td> <table id="divToken"> <tr> <td><image class="token1 money" id="tk1" value="1"></image></td> <td><image class="token5 money" id="tk5" value="5"></image></td> <td><image class="token10 money" id="tk10" value="10"></image></td> </tr> <tr> <td><image class="token25 money" id="tk25" value="25"></image></td> <td><image class="token50 money" id="tk50" value="50"></image></td> <td><image class="token100 money" id="tk100" value="100"></image></td> </tr> </table> </td> <td> <div style="color:white; margin-left:60px;"> Kasadaki Para(TL):<br /> <input type="text" readonly id="cash" value="@Model" style="font-size:50px; color:green; width:130px" /><br /><br /> Ortaya Konan<br /> <input type="text" readonly id="putCash" value="0" style="font-size:50px; color:blue; width:130px" /> </div> </td> </tr> </table> </div> </td> <td class="column"><!--<img src="~/Images/casino.jpg" class="displayed" />--> <div class="middleimagecontainer"> <img src="~/Images/casino.jpg" class="middleimage" /> </div> </td> <td class="column" style="vertical-align:top; padding-top: 40px;"> <div style="color:red">Masa Score:</div> <input type="text" readonly id="score2" value="0" style="font-size:100px; color:red; width:120px" /> </td> </tr> </table> </body> </html> |
Ortaya para amaçlı konan fişlerin Css Sprite’ı aşağıdaki gibidir. Yani fişler için de tek resim kullanılmış ve belirlenen kordinatlara göre ekrana basılmıştır.
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 |
.token1 { background: url('/Images/token.jpg') no-repeat; width: 60px; height: 62px; } .token5 { background: url('/Images/token.jpg') no-repeat -90px 0px; width: 60px; height: 62px; } .token10 { background: url('/Images/token.jpg') no-repeat -180px 0px; width: 60px; height: 62px; } .token25 { background: url('/Images/token.jpg') no-repeat 0px -60px; width: 60px; height: 62px; } .token50 { background: url('/Images/token.jpg') no-repeat -90px -60px; width: 60px; height: 62px; } .token100 { background: url('/Images/token.jpg') no-repeat -180px -60px; width: 60px; height: 62px; } |
Jquery Scriptler: Gelin tüm functionları tek tek inceleyelim.
1-) $(document).ready():
- “.money” css li para fislerinin üzerine gelinince mouse’un el işaretine gelmesi sağlanır.
- İlgili fislerin tıklanması durumunda, kasadaki paranın “#cash”‘in 0’dan büyük olmasına ve ortya konan para “#perCache”‘den fazla olup olmamasına bakılır. Kısaca oyanancak yeterince para olup olmadığına bakılır.
- Kasadaki paradan, ortaya konan para çıkarılır ve ortaya konan paranın yeni değeri tıklanan paranın değeri kadar eklenir.
- Son olarak “Oyuna Gir” (#enter) buttonu gizlenir.
2-) PostData(_isWin):
- Gönderilecek data paketi “_data” tanımlanır. İlgili kazanılan ya da kaybedilen para “putCache“, client’ın unique sessionID’si “session” ve son olarak kazanıp kazanamadığına ait bilgi “isWin“.
- Yukarıda tanımlı webapi servisine azure tarafında yandaki link ile “http://*********.azurewebsites.net/api/bank/” “ajax.Post” sayesinde erişilir. İlgili paket json formatında gönderilir.
- Başarılı işlem sonucunda “_isWin” ile kazanılma veya kaybedilme durumu “alert()” ile belirtilir. Ve sayfa refresh olarak ilk konumuna getirilir. “location.href = location.href;“
3-) GetNewCard(player):
- $.post(“/Home/GetRandomCardHtml”) ile player parametresine göre ya oyuna yeni girişte (player==1)ya da yeni kart çekilişinde (player==3) player için servisden random kart getirilmesini sağlar. İlgili servise kodun ilerleyen kısımlarında bakılacaktır.
- Ortaya para konduktan sonra Oyuna Girişte (player==1) durumunda “Oyuna Gir” buttonu gizlenir. “Yeni Kart Çek” buttonu gösterilir. Ayrıca para fislerinin tıklanması kapatılır. Son olarak serviden dönen data yani çekilen kağıtlar “cardContainer” divinin içine Append ile eklenir.
1 |
$(".money").off('click');$("#enterButton").hide();$("#newCardButton").show();$("#cardContainer").append(data); |
- “player==1” durumunda yeni girişte toplam 2 kağıt çekilir. Yani player1 için 2 defe servise gidilir.
- *Eğer çekilen kart tipi
“A” ise yani value’su 1 ise genel toplamın “$(‘#score’).val()” 21’i geçmemesi durumunda 11 değerini, genel toplamın 21 geçmesi durumunda 1 değerini alması sağlanır.
- Eğer ilk gelen kart “As” değil ise çekilen kart değeri “parseInt($(this).attr(‘value’))” ile bulunup toplam score ekranına yazılır “$(‘#score’).val()”
- Eğer “player==3” yani “Yeni Kart Çekin” button’una basılınca ilgili servisden tek bir kar çekilir. Ve ilk kart “As” için gene yukarıda yazılan case adımları gerçekleştirilir.
- Her iki durum için “$(‘#score’).val()” score’un 21 olması durumunda “PostData(true)” şeklinde ilgili webapi servisine değer gönderilir. Yani oyun kazanılır. Score’un 21’den büyük olması durumunda “PostData(false)” şeklinde ilgili webapi servisine değer döndürülür. Yani oyun kaybedilir.
- Son olarak eğer “player==2” ise, bu da pc yani masa için random bir kart çekilmesi anlamına gelmektedir. Yukarıda tanımlı tüm kurallar burda da aynen geçerlidir.
4-) Stay() : İlk kez ortaya para konup kart açıldıktan sonra, basılabilen Kal button’unda çağrılan functiondır. Bir diğer çağrıldığı yer ise bilgisayar yani masa tarafında, client “Kal” buttonuna tıkladığı zaman, client’ın score’undan yüksek – 21’den fazla olmamak kaydı ile sürekli kart çekmesini recursive olarak sağlandığı yerdir. Yani Stay() function ‘ı koşul sağlandığı sürece kendini tekrar tekrar çağaracak ve Masa için çekilen random kartları ekrana basacaktır.
- İlk koşul olarak gelen kart
ise yine belirlenen koşula göre toplam score 21’den büyük ise 1 değil ise 11 ile toplanır.
- Masa için toplam sonucu 21’den büyük olur ise “PostData(true)” şeklinde gönderilip Player1 yani client’ın kazandığı duyurulur.
- Eğer 21 veya player1 score’dan büyük ise “PostData(false)” şeklinde Player’in kaybedip, masanın kazandığı duyurulur.
- Eğer Score1 ve Score2 eşit ise “Oyun Ortada” yazılır.
- Eğer Score2 21’den ve Score1’den küçük ise “Stay()” function’ı yine çağrılır ve Masa için random yeni bir kart çekilir.
İlgili javascript kodları aşağıdaki gibidir :)
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
<script> $(document).ready(function () { $(".money").hover(function () { $(this).css('cursor', 'pointer'); }); $(".money").click(function () { var cache = parseInt($("#cash").val()); var perCache = parseInt($("#putCash").val()); var per = parseInt($(this).attr("value")); if (cache > 0 && cache >= per) { $("#cash").val(cache - per); $("#putCash").val(perCache + per); $("#enter").removeAttr('disabled'); } }); }); function PostData(_isWin) { var _data = { "putCache": parseInt($("#putCash").val()), "session": $("#hdnSession").val(), "isWin": _isWin }; $.ajax({ type: 'POST', url: 'http://*********.azurewebsites.net/api/bank/', data: JSON.stringify(_data), contentType: "application/json; charset=utf-8", traditional: true, success: function () { if (_isWin) { alert("Player 1 Kazandı."); location.href = location.href; } else { alert("Masa Kazandı.."); location.href = location.href; } } }); } function GetNewCard(player) { $.post("/Home/GetRandomCardHtml", { Player: player }, function (data) { if (player == 3) { $("#cardContainer").append(data); //Check Data $('#score').val(0); $('#cardContainer').children('img').map(function () { if (parseInt($(this).attr('value')) == 1) { var scr = parseInt($('#score').val()) + 11 > 21 ? parseInt($('#score').val()) + 1 : parseInt($('#score').val()) + 11; $('#score').val(scr); } else { var scr = parseInt($('#score').val()) + parseInt($(this).attr('value')); $('#score').val(scr); } if (parseInt($('#score').val()) > 21) { PostData(false); //alert("Player 1 Kaybetti..") // alert("Masa Kazandı.."); //alert("Table Kazandı..") // location.href = location.href; } else if (parseInt($('#score').val()) == 21) { PostData(true); //alert("Player 1 Kazandı..") // alert("Player 1 Kazandı."); // location.href = location.href; } }); } else if (player == 1) { $(".money").off('click'); $("#enterButton").hide(); $("#newCardButton").show(); $("#cardContainer").append(data); //First Time $('#score').val(0); $('#cardContainer').children('img').map(function () { if (parseInt($(this).attr('value')) == 1) { var scr = parseInt($('#score').val()) + 11 > 21 ? parseInt($('#score').val()) + 1 : parseInt($('#score').val()) + 11; $('#score').val(scr); } else { var scr = parseInt($('#score').val()) + parseInt($(this).attr('value')); $('#score').val(scr); } }); //Second Time $.post("/Home/GetRandomCardHtml", { Player: player }, function (data) { $("#cardContainer").append(data); //Check Data $('#score').val(0); $('#cardContainer').children('img').map(function () { if (parseInt($(this).attr('value')) == 1) { var scr = parseInt($('#score').val()) + 11 > 21 ? parseInt($('#score').val()) + 1 : parseInt($('#score').val()) + 11; $('#score').val(scr); } else { var scr = parseInt($('#score').val()) + parseInt($(this).attr('value')); $('#score').val(scr); } }); if (parseInt($('#score').val()) > 21) { PostData(false); //alert("Player 1 Kaybetti..") //alert("Masa Kazandı..") // alert("Masa Kazandı.."); // location.href = location.href; } else if (parseInt($('#score').val()) == 21) { PostData(true); //alert("Player 1 Kazandı..") // alert("Player 1 Kazandı.."); // location.href = location.href; } }); // //Table Cards $.post("/Home/GetRandomCardHtml", { Player: 2 }, function (data) { $("#cardContainer2").append(data); //Check Data $('#score2').val(0); $('#cardContainer2').children('img').map(function () { if (parseInt($(this).attr('value')) == 1) { var scr = parseInt($('#score2').val()) + 11 > 21 ? parseInt($('#score2').val()) + 1 : parseInt($('#score2').val()) + 11; $('#score2').val(scr); } else { var scr = parseInt($('#score2').val()) + parseInt($(this).attr('value')); $('#score2').val(scr); } }); }); // } else if (player == 2) { $("#cardContainer2").append(data); //Check Data $('#score2').val(0); $('#cardContainer2').children('img').map(function () { if (parseInt($(this).attr('value')) == 1) { var scr = parseInt($('#score2').val()) + 11 > 21 ? parseInt($('#score2').val()) + 1 : parseInt($('#score2').val()) + 11; $('#score2').val(scr); } else { var scr = parseInt($('#score2').val()) + parseInt($(this).attr('value')); $('#score2').val(scr); } if (parseInt($('#score2').val()) > 21) { PostData(true); //alert("Player 2 Kaybetti..") //alert("Player 1 Kazandı..") // alert("Player 1 Kazandı.."); // location.href = location.href; } else if (parseInt($('#score2').val()) == 21) { PostData(false); //alert("Masa Kazandı..") // alert("Masa Kazandı.."); // location.href = location.href; } }); } }); } function Stay() { $.post("/Home/GetRandomCardHtml", { Player: 2 }, function (data) { $("#cardContainer2").append(data); //Check Data $('#score2').val(0); $('#cardContainer2').children('img').map(function () { if (parseInt($(this).attr('value')) == 1) { var scr = parseInt($('#score2').val()) + 11 > 21 ? parseInt($('#score2').val()) + 1 : parseInt($('#score2').val()) + 11; $('#score2').val(scr); } else { var scr = parseInt($('#score2').val()) + parseInt($(this).attr('value')); $('#score2').val(scr); } }); if (parseInt($('#score2').val()) > 21) { PostData(true); //alert("Player 2 Kaybetti..") // alert("Player 1 Kazandı.."); // location.href = location.href; } else if (parseInt($('#score2').val()) == 21 || parseInt($('#score2').val()) > parseInt($('#score').val())) { PostData(false); //alert("Masa Kazandı..") // alert("Masa Kazandı.."); // location.href = location.href; } else if (parseInt($('#score2').val()) == parseInt($('#score').val())) { //alert("Oyun Ortada..") alert("Oyun Ortada.."); location.href = location.href; } if (parseInt($('#score2').val()) < 21 && parseInt($('#score2').val()) < parseInt($('#score').val())) { var delay = 500; //0.5 seconds setTimeout(function () { //console.log("Time:" + $.now()); Stay(); }, delay); } }); } </script> |
Backend tarafında neler yapılıyor, gelin hep beraber inceleyelim:
HomeController:
- Öncelikle rastgele card seçimi yapılacağı için Random() nesnesi üretilir.
- Client’ın SessionID değeri çekilir.
- Bu sessionID değerine göre “BankRestServices” WebApi servisi ile ilgili client’ın para bilgisi çekilir. Çekilen data, model olarak “Index.cshtml“‘e gönderilir.
- “GetRandomCardHtml()“: Bu method’da “Player” parametresi ile client’mi yoksa bilgisayar mı için kart çekileceği belirlenir.
- “AllCards()” Tüm kartların Value değeri ve Code Snippet‘e ait css leri ile tanıtıldığı “List<Card> CardList” şeklinde bir dizinin dönüldüğü methoddur.
- “GetRandomCardHtml()” methodu ile ilgili AllCards() methodundan random bir Card çekilir.
- “CheckBeforeCards()” Methodu ile ilgili client’ın bu oyun boyunca çektiği kartlar “Session[“Player_” + Player]” sessionda saklanır. Eğer önceden bu kart çekilmiş ise bir daha çekilmemesi için geriye false dönülür. “while(result)” ile eğer aynı kart çekilmiş ise recursive olarak yeni bir kartın çekilmesi sağlanır.
- “Card” sınıfı Css ve Value propertyleri ile aşağıdaki gibi tanımlanmıştır.
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; namespace _21Games.Controllers { public class HomeController : Controller { static Random rnd = new Random(); // GET: Home public async Task<ActionResult> Index() { Session.Remove("Player_1"); Session.Remove("Player_2"); ViewBag.Session = HttpContext.Session.SessionID; BankRestServices services = new BankRestServices(); var model = await services.GetCache(HttpContext.Session.SessionID); return View(int.Parse(model)); } public string GetRandomCardHtml(int Player) { Player = Player == 3 ? 1 :Player; bool result = true; string html = ""; while (result) { //return "<image class='spritesA' value='1'></image> "; int r = rnd.Next(AllCards().Count); var card = (Card)AllCards()[r]; if (CheckBeforeCards(Player, card.Css)) { result = false; html= "<image class='" + card.Css + "' value='" + card.Value + "'></image> "; } else { result = true; } } return html; } public bool CheckBeforeCards(int Player, string Css) { if (Session["Player_" + Player] == null) { List<string> Cards = new List<string>(); Cards.Add(Css); Session["Player_" + Player] = Cards; return true; } else { List<string> Cards = (List<string>)Session["Player_" + Player]; if (Cards.Contains(Css)) { return false; } else { Cards.Add(Css); Session["Player_" + Player] = Cards; return true; } } } public static List<Card> AllCards() { List<Card> CardList = new List<Card>(); CardList.Add(new Card(1, "spritesA")); CardList.Add(new Card(2, "sprites2")); CardList.Add(new Card(3, "sprites3")); CardList.Add(new Card(4, "sprites4")); CardList.Add(new Card(5, "sprites5")); CardList.Add(new Card(6, "sprites6")); CardList.Add(new Card(7, "sprites7")); CardList.Add(new Card(8, "sprites8")); CardList.Add(new Card(9, "sprites9")); CardList.Add(new Card(10, "spritesB")); CardList.Add(new Card(10, "spritesK")); CardList.Add(new Card(10, "spritesP")); CardList.Add(new Card(1, "spritekA")); CardList.Add(new Card(2, "spritek2")); CardList.Add(new Card(3, "spritek3")); CardList.Add(new Card(4, "spritek4")); CardList.Add(new Card(5, "spritek5")); CardList.Add(new Card(6, "spritek6")); CardList.Add(new Card(7, "spritek7")); CardList.Add(new Card(8, "spritek8")); CardList.Add(new Card(9, "spritek9")); CardList.Add(new Card(10, "spritekB")); CardList.Add(new Card(10, "spritekK")); CardList.Add(new Card(10, "spritekP")); CardList.Add(new Card(1, "spritemA")); CardList.Add(new Card(2, "spritem2")); CardList.Add(new Card(3, "spritem3")); CardList.Add(new Card(4, "spritem4")); CardList.Add(new Card(5, "spritem5")); CardList.Add(new Card(6, "spritem6")); CardList.Add(new Card(7, "spritem7")); CardList.Add(new Card(8, "spritem8")); CardList.Add(new Card(9, "spritem9")); CardList.Add(new Card(10, "spritemB")); CardList.Add(new Card(10, "spritemK")); CardList.Add(new Card(10, "spritemP")); CardList.Add(new Card(1, "spritekaA")); CardList.Add(new Card(2, "spriteka2")); CardList.Add(new Card(3, "spriteka3")); CardList.Add(new Card(4, "spriteka4")); CardList.Add(new Card(5, "spriteka5")); CardList.Add(new Card(6, "spriteka6")); CardList.Add(new Card(7, "spriteka7")); CardList.Add(new Card(8, "spriteka8")); CardList.Add(new Card(9, "spriteka9")); CardList.Add(new Card(10, "spritekaB")); CardList.Add(new Card(10, "spritekaK")); CardList.Add(new Card(10, "spritekaP")); return CardList; } } public class Card { public Card(int value, string css) { Value = value; Css = css; } public int Value { get; set; } public string Css { get; set; } } public class BankRestServices { public async Task<string> GetCache(string session) { string uri = "http://blackjackservice.azurewebsites.net/api/bank?session=" + session; using (HttpClient httpClient = new HttpClient()) { HttpResponseMessage response = await httpClient.GetAsync(uri); return await response.Content.ReadAsStringAsync(); } } } } |
Azure publish için birçok yöntem mevcuttur. Ben öncelikle ilgili web site’ı Azure tarafında oluşturdum. Daha sonra ilgili Web site’a ait https://portal.azure.com adresinden yukarıda görülen publish profile’ı indirdim. Daha sonra ilgili profile’ı aşağıda görüldüğü gibi import edip, Azure’a publish işlemini yaptım. Ayrıca ilgili DB de gene Azure tarafında yaratılıp, cloud’a ait connection strings alınmış ve Sql serverda Azure tarafına bağlanılıp ilgili tablolar yaratılmıştır.
Bu makalede farklı teknolojiler ve kullanıcı etkileşimi ile basit bir oyun nasıl yazılır, hep beraber inceledik. Performans için, yapılması gerekenleri hem front hem de backend tarafında detaylıca kodladık. Azure tarafında Debuging ve Publish olaylarını inceledik.
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Çok güzel olmuş elinize sağlık hocam
Çok teşekkür ederim…
İyi eğlenceler :)