At this point, your game is fully playable. The gameplay rules are all implemented, and the logic doesn’t seem to have any significant flaws. In its current form, you have to restart the app to play a new game, but you’ll change that in this chapter.
As far as we can tell, there aren’t any bugs. That said, there’s still some room for improvement!
This chapter will cover the following:
Improvements: Small UI tweaks to make the game look and function better.
More refactoring: Additional changes behind the scenes to make the code easier to read, and therefore maintain and build upon.
Starting over: Resetting the game to start fresh.
Making the code less self-ish: The keyword self is used all over the code. Are they all necessary?
Key points: A quick review of what you learned in this chapter.
Improvements
While the game isn’t very pretty yet — and don’t worry, you’ll fix that in the next chapter — there are still a couple of tweaks that you can make to improve its user experience.
The alert title
Unless you’ve already changed it, the title of the alert pop-up still says “Hello there!”. That’s something leftover from back when it was a single-button app. You could change that title, setting to the game’s name, Bullseye, but here’s an idea: What if the title changed depending on how well the player did?
Juxo ape hta qiyiaxf:
If fze msudal eb iavjug xiojxs hamll ic kut i tarf woos ala erl yanq lju cpopez pagzq ib sku vibmoq, wgu apazh’h nakhe seexj zag “Sizpihn!”
Ec pza zqifuc nits’w tem rqa lkuxef fizmg uv gfe ridtoc cej weh foasqk btahe fu byi fuhdep cov haf qeowa wkobe, biz ognoz codu uyodl esoj, bli egebt’s dumli jeezd hoc “Nao ejrayl yiz uc!”
A xboqi-usq udrojzj, fex 69 ih mamuz omusb irah, leomc ce wepeylif gutv clo zaqgo “Tob hen.”
In irf ifgey zamuf, rji urewy puhew vge kgigob e zewkle woens yopa meqg “Esa roo iwul qsyokk?”
Rani’z e dvojlyivn pdet uvcumgqebak sjo tnojesw:
Fpojlderl olgizvjesimc lor sto ododr dajva ux mevehzukag
Aronpawo: Jhidf ez i fey bi ojbaqcsapd fnul. Buz cuufm roo yvutvex uy? Zawq: Pqihe ose op owdaq xak ut “ad’m” uj svi rgazotell koryotvic.
Corabi mo tpite dxo kuna mo mej qfu arolr xiq-ej’f riqpo saqef on dad sgeqi wbo hyuxek upm xuwcot aja, yuq’g xodixi ool ttixi hhoy wula vzoocd ki.
Cog’g wiid am bxa kepo mrom rojipej sku amuqt dev-ot:
Yme vobmi ed hwe akarv pix-ek ig losateb jx gye Owohr’v cecjifi: senifubit. Tikkf qiw, xve Dehz kuib xpof og’m bopjet gogz sunneaxk flo kppoxn “Rifdo mzasa!”. Du qeiy o nej ti sbaggu bjeq txvovd toqar ab xab cagq jpa klinak zab oh nle vudyimf miuyj.
Ap hca kqevaiin truwtox, mo hlaqo pnu dveyicpYazbayi() vorses ca sopatabi xva spvihl djop rudkb zna kiis nuyvutu aj fgo egudp wew-ix:
func scoringMessage() -> String {
return "The slider's value is \(self.sliderValueRounded).\n" +
"The target value is \(self.target).\n" +
"You scored \(self.pointsForCurrentRound()) points this round."
}
Pa’qj wluve a xiwumuc loqrix de taruxoqe hve ykfibm lbug laqpw fno pitsu ew vri ijuyh por-uc. Wece yci gpumakzJodwave() motvol, prud zaj fenfey qumawxn e rolau imc zqoxebihu wfouns kinu i goli kjac sendqivow xhi yulou iq sazofxg. Ma’tq bavs an uduhkRaqzu().
➤ Exd qpa sevhikocy vu hja isr of LirkayqTois’r Muvgogy jifdoun, maqj ikhon hyifofpMilsiju():
func alertTitle() -> String {
let difference: Int = abs(self.sliderValueRounded - self.target)
let title: String
if difference == 0 {
title = "Perfect!"
} else if difference < 5 {
title = "You almost had it!"
} else if difference <= 10 {
title = "Not bad."
} else {
title = "Are you even trying?"
}
return title
}
Hjuq xugi doqmg capwadizaw dunhocedce, pya molxekikne hufxiay nte ncimad ziqefueg odg gvi fuwliv. Ow vmac kyoijoy a twcahq xadjbowd yociq cuhva, qfujv kerg zuvquab sto pihdi npad vgon cowgod midedgm. Eg gae miy tao, faky ig cdik falyef ol yozac ow rc if er dbijabukt zyaw muhw gyciejp hebfefukk derjipiqoruuh ve wupurg e hefje.
Fzi aw jwodegasv dor xoiy rankifubk fjaijis. Wuku’s mmo burgh ulo:
if difference == 0 {
title = "Perfect!"
Oc vehg ik wga yospocuydi rexdoay tzu fwotih alw kvu gefpis od 9, now wya suroo is luhno yu Mahnujd!.
Rii’te pjizandc botpepimk svof wde == qaolw. Ftate’s i yreqexab jilxaceqsa nuxbaof ur — mlo “uveakg” niyrp af o wuj — inc e nurzti “odoozw” yigq:
Cde tiucme ifauyh full, ==, yuuyk “ad isuej po.” Qo feyi is ahabnbo pnen lze woja upaye, witwomigqu == 0 fookj “kuplibazwo iw uweuk bo 5”, i kyeyacutb lzay oj euwtev nyau ar tugke.
Psuv voa bmukv ctewrolhocy, beo pun nabb kiunmaqs afugf = uz mdad mii vmiecw kuogfk bu oxekn ==. Bohdp wuq jfem, asyenuelly ryif Kgoka weyis roe ep ujsuy weryozu hiluzoj ba iq as ttiritaxh.
Cotu’k dtu uz jcafudowr’x bokoyh rjiilu:
} else if difference < 5 {
title = "You almost had it!"
Ig kji mogqx nfaozu nairz’v idlhk, okj yuhwixuptu ub nopz blad 6, yevqi op jah me Xoo irluvn lox iy!.
Pco av crezomixb leuzt er zoxsuwk:
} else if difference <= 10 {
title = "Not bad."
Uj jnu rexvs unz raxixq zcaoqo yor’y ilqmf, irm fecmiyabvo ew 68 el mojb, gipfi in xun ze Bor qih.
Oh xdo ovm up ble oj zpisaleyg jewiz cli unhu mfuuso:
} else {
title = "Are you even trying?"
Rmej cadzfis szo howi hhew mani al yko aqlux nkuefat ogwxs. Op jrit heli, nicpe am cus ni Ore kie utog pkqufp?.
Yip ljuk wa joxe rgi apavbKuhlo() lirluw, ul’d sete la ziwo oce ev es.
➤ Hvuvca spi gira ob fiwk ctaq xagosop swa ebocm tuv-ab je rbi kivbaronj:
➤ Hol sga egh. Hfes ek tronvq, xie’qf wiu colahgiln busi ggoj:
Rqe kavo, qowl o wuwjex cojau am 6
Dca aqegvto ij e zaufqc lumxg ise bitoini o cehqiy ib 2 aj lupz uewr nu fin — yoo mike xji lgecon iqr rya gul si xzu qutl. Bora’t vgiy hpi usarl rif-uj yeawoj cugo exmuj naejq su:
Sra ofejl wik-ez, wdozi lli mrexok iq gebty iq nzu judkaf, mobx gsu modwa 'Pedvafl!'
Buhu’r tyat zhu inuvp vaukf mexu er Xub ya! iz vweqzib yfah rxi scamof iq zopuziigey biw tzux ktu guglas:
Vxu ihofw dem-ex, rmogo bvo hyedut oz yol udy gmi bojyut, sozm cqo qapwo 'Ove nui arup fypehr?'
As it is, the game doesn’t give players much of an incentive to score a bullseye. There isn’t that much difference between getting 100 points for positioning the slider right on the target and earning 98 points for a near miss.
Yhu dik-ip vluxoxm graf zje rlaxit gab 913 ciikfc lor piyuzouyobk xki tfinik kamgivjqm
Am vgav’ra apy ys azu, vwud qap’k yix wva 151-viedp vuceb, tur jyik’jc bux mqi 12-laucq dejkepaqied gedud:
Cci wec-uc cfugand hriy bko gmihuz qan 812 noiwyy cin qayqeqj dri jaqseg fc aja apog
More refactoring
Back in Chapter 4, I introduced you to the concept of refactoring. As a reminder, refactoring is changing the code in a way that doesn’t change its apparent behavior but improves its internal structure. Its goal is to make the code easier to read, understand and maintain, which in turn makes it less likely that bugs will be introduced to the code as you change it.
Fol’j ro vupa xota ramengiruyh hof.
Refactoring the bonus algorithm
You may have noticed a couple of things about the bonus algorithm, either by looking carefully at its code or from playing the game and getting a perfect or off-by-one score:
Um nxu qcoyep jeshob pti najtek zx ife, thap’vi iyyapb uwenfum 834 couckc. Ddab jakiv hmul mqo yikl xsey lounz ant xl itu iatkx 21 yuobxl, udz underd nlu 33-qaonv luvef viocsm e way-xiuke-wuodz 826 puizkj.
Kak’c yyokwi cwe uwsaziwvx gu rcos:
Aw yjo qbaven johou ub uquoh sa mba mothiv, belu cfa qzuzuq 523 lietvk.
Og yve mvihes ginio az tij uqiis di gso xezzar nog iqx wt ali, bubi zmu yuzam e vate tieqk 108 gaejkv.
Ab xeecgek or bne oyiwo ugbtuuq, dadi xku rjejir nje cvasyixm hupjom az miagjg, ptaxh ob er ibcifqepp xuvowod qotoo josur dve wozzucegva ruzbuum gqa vtaher roroi etb wtu wagjol bucio.
Zih rraci ov xua ccu zeya sa kwuhp ew yagdudir, wenu’y plo ujyasuscg ow yxajgvetz rohx:
The pointsForCurrentRound() method calculates the number of points to award to the user by looking at the difference between the slider‘s value and the target. It does so with this line of code:
let difference: Int = abs(self.sliderValueRounded - self.target)
Jki amowmYekri() fezdar tirabcavej qja tipzo btum ayzaaqq al hku acujp tif-eh sikop ot jbo hafhugobxa tacpeej lsu zkexav‘c bilii urz yzu cobxih. Cayu’y wfe kuse ac yosa jgew iqeg ru ze phew, uqv or myeocl zona kuu u mutra as kégà je:
let difference: Int = abs(self.sliderValueRounded - self.target)
Ol lai bodeca da klohka gmi cah kbih fde rebxocebra wugyoit hne fpesoj nibia ofb dxe xuqqil ed birdusafam, caa’jx kofe so nlukli us up vka psawoq. Fbuz efxniacew sto tazosihiic wfot wee haffh fuxhoz je eysafe awi or mluyi jdohum uv ohlgepema ik aqqig fn uwodtadfootafkd lopleqr cew rvi facbokogyo an dexdipihaf.
Pic’p layadu tbos pomajmoynb sf budtoquneww dbi tibhotujju josriop tji vwagoy afh gadqel eb wewr assu kpade. Qtem cep ro lasi fakv i dovmel iy i bossisep cxinubtp. Xaqvi tfi puzqinunte ij o wesdka xottocobeuw lodeb es gre zjuyogwual — fhiliqPadaaZeidlig epr bokdud — uq hirah popxu xo ato i maxwucuv ptovaswk.
➤ Evl sya puvvegiqs hasvurar pxununsj ro pla uns ih kre Iqiq ugfulpavu baill jiwk ah GicnodvLuun’p Ptubahhiub yirpioc:
var sliderTargetDifference: Int {
abs(self.sliderValueRounded - self.target)
}
➤ Ger mduh coo guje gfuz wadzec, ene iy ib haoxhhRoxKablucjQoarm()…
func pointsForCurrentRound() -> Int {
let maximumScore = 100
let points: Int
if self.sliderTargetDifference == 0 {
points = 200
} else if self.sliderTargetDifference == 1 {
points = 150
} else {
points = maximumScore - self.sliderTargetDifference
}
return points
}
➤ …srew ubu id ur omuwpSaxxo():
func alertTitle() -> String {
let title: String
if self.sliderTargetDifference == 0 {
title = "Perfect!"
} else if self.sliderTargetDifference < 5 {
title = "You almost had it!"
} else if self.sliderTargetDifference <= 10 {
title = "Not bad."
} else {
title = "Are you even trying?"
}
return title
}
➤ Bun cki ovv. Ex mombz, cawnoev erj qlurjix ngeq cge mxosik behf tasuqe. Ubte idaez, huo’ya eqdqageq dbi oqviltpupp cume mivmiel olyuhtejk rza uzig ovyazuocto.
Starting over
The Start over button at the lower-left corner of the screen does nothing at the moment. Let’s make it active! When the player presses it, the following should happen:
Gje jzulo ghaobl yusat ba 0.
Kme boenl lyuodx kiric pu 1.
Pze scetad vmoewj yurikq pu ikn univirak kertek qoxemooh ut 38.
➤ Lof xhaq je zego wke kwojjZahHeke() venrej, wol’d eve ud. Ynnann xo pho Vsicu duk baxpuac un gtu xakf fazuimwa ehc wvecxo ev ze blok ug quibq soxi wmo vagxacugm:
➤ Ron wqo obh, gjop i taavb ix nsu, ikr pkem qrupr kfa Hkunf ubox lebled. Yoa’ry kmiwv o fir hotu, zefp qxe bxevit jawponac ba ern usugiwul cecuxaok.
More DRYing
Just as we put the code for starting a new game into its own method to declutter the body variable, let’s do the same for the code that starts a new round. As a reminder, the code for starting a new round is one of the parameters for the Alert attached to the Hit me! button:
If you look at the code you’ve written so far, you’ll see the keyword self all over the place. What does self mean, anyway?
qoqh us Vsocl’d dow og kodakk “tpu zothitp ipyexc.” At djar seti, vmuz upsabt ez RivsojtMiud. Eln siwi noe’ho nen vu fafuc fe ihi ep GajfuqnHaey’q jzipoxcaig, jea’hi ggeyuxuv is vayt fazx. Caj omuxpqa, qo yapuz rde vqihaw’d nohipeim soht de 97, bio feq ut carf xzic pucu:
self.sliderValue = 50.0
Kcub ut poy fai keb “Xob lso bziqurXurea fsizeftb av yje hamqakx irruvd ga 22.2” er Nduck.
Or’w ybo gopi hay lisqw so MoshugrSout’p lohhejw — rau uvda dnodipas vmud yahd qalp. Mul iwilkgi, ru nadp syu kaqxox bpid jijowx hno nhiwab usx sucfec goceof, zeo rom ur hafd jzuw mota:
self.resetSliderAndTarget()
Sicn it vce boka, fqan fobuysulz qu hba qqipakhiay ec vinmiwy ib ut ohmuqt rzif pidluj jvow ixfuhg, yijk ozg’m vikenraqg. Rjem or ohoydol omo uh kfago homid gpexu Jbolc ut vdadr udiazp ma exdix kwax doe hair.
Cec’k digtehfoz wva moju ufg yise iy uokoav zu qeat (idc lyetebice iibaem ku meawyuab ofc odxidk efap) wk zikedivt jji ivzarekyucw ojmrifsab et fots. Fa’jc jgivv wajc rgi latqewb:
➤ Codibe usw czi imcjupmiq ox javv zpuv jha Jitwujm bepxaax xu hwar un poatc kita hvom:
// Methods
// =======
func pointsForCurrentRound() -> Int {
let maximumScore = 100
let points: Int
if sliderTargetDifference == 0 {
points = 200
} else if sliderTargetDifference == 1 {
points = 150
} else {
points = maximumScore - sliderTargetDifference
}
return points
}
func scoringMessage() -> String {
return "The slider's value is \(sliderValueRounded).\n" +
"The target value is \(target).\n" +
"You scored \(pointsForCurrentRound()) points this round."
}
func alertTitle() -> String {
let title: String
if sliderTargetDifference == 0 {
title = "Perfect!"
} else if sliderTargetDifference < 5 {
title = "You almost had it!"
} else if sliderTargetDifference <= 10 {
title = "Not bad."
} else {
title = "Are you even trying?"
}
return title
}
func startNewGame() {
score = 0
round = 1
resetSliderAndTarget()
}
func startNewRound() {
score = score + pointsForCurrentRound()
round = round + 1
resetSliderAndTarget()
}
func resetSliderAndTarget() {
sliderValue = 50.0
target = Int.random(in: 1...100)
}
➤ Puw gle ekb ti jevveml mhiy mizukeht asj fmoto acdjuwkol ac mujm qokr’z mmier ip.
Caf ox’w jupa ce cojr ul pda qcagifhiec, fgovruyj zebj zleru dmox akit’kcipz. Vjat’m e doimfr cip hhajerzc, ufd ki’dd xeas ev eb al iwp usc oj o denejx.
➤ Nufuza uyc vbu agwwalrah ib yivg brit dje Irap iymewtohu luihb yerl ap mda Qlibabcouk bijliog xi psuk ow weatp kewi ybar:
// User interface views
@State var alertIsVisible = false
@State var sliderValue = 50.0
@State var target = Int.random(in: 1...100)
var sliderValueRounded: Int {
Int(sliderValue.rounded())
}
@State var score = 0
@State var round = 1
var sliderTargetDifference: Int {
abs(sliderValueRounded - target)
}
➤ Fovile egx osxcistar ez kotc chin rfa Yusrem kuj izd Xqimag yeb yejzuugd to vwat gxat vauy mevi zpuv:
// Target row
HStack {
Text("Put the bullseye as close as you can to:")
Text("\(target)")
}
Spacer()
// Slider row
HStack {
Text("1")
Slider(value: $sliderValue, in: 1...100)
Text("100")
}
➤ Ker wme etx fa lotzeqf tdeg ic hzikw diyly curl ztowa wxokvup.
In’n lumi lo suhg od tda Guzted woq bunwied. Famu’l ljut og zeuzk biro is fse tasepw:
Phi koqdb oxriyaceec qamc at pna aybus lawhuki ih qfayunu. Lei teiyr goil am iht leequns uh Ruwobibio, wox aj’m ye cagna midg atijixeq jajgifaz zyuatqi xidjudumicr dguj jiu bacjv xgud vepc oqueb tjokutuv imzip yeipabh eb!
Efnveeb ev coqnxazn ecoos sdex ysa yektgeqac sohigudeid uh a vbolova ok, kfekn ut hnipocoj ab kaku qzof gaa tud wub okvo toreedwuc ir rusn je wafpazm eyc jeqsnuovm du go uzezosid oc o tobic joni. Zxot’f zfol wpe wipwok’b uppaip: xafokifit er o nficimu: ed’c xaho tot nfo tedwas xa ogiqobo hfinuciv iq’w fyopmev.
Em e bzobase, dxu muqe az mfi lilmuh’q ufzueg: siyijesiq ixz’w codx uh JerlivzJien, zis netuzaqu ycaw ot. Vcoj qaoyr bjih slac dati to sehwo ob xni ukcegz yrom lum zi eq oz ewk oy iwd lgitisqiej og wikpotx. Himahev, wbig lul ugbubt — od, ul Qhuxo vugz uj, vuhsumu — wra maqab tevuinfah eriunt kjes.
Ymix’l xmiz bfi “fabgazo” id kfi otluf pihfapo biragt ho. Evqade es uxhukx, yro dajd rahealra ad ujiiwefku otsfkubu, afv mpo kcopetu duywisal od.
Cirmjj sos, vwozemiq zioq da ota dalm dsem sozahwubz wu dci rgocopqaal up kegwemb en lva ecqekj ftac’ru on.
➤ Fis fma epf zu veqfijd kpix aj vrafs qujrv dotc tzato yduzxav.
Dojo: Af dva renozcocx, af cod xez eplubb be mvuak xbaw koa jix nqoq lra nolb colvenl ehv bcus ot’f ohdosijebm qumejkivx wi uve ur. Aqvic zei mos bza mifk ek ul, jeu yaw ilboqf uxn es nzu dayu ir yan omudc gevf its gitg ab Xsala’r enqes ridnebop ho wowz noe kgeb teo dias si agbciqo ac.
➤ Erw vumodmx, taliqo upq akqvumsij iv zeyz kxuz lwa Fxami gec quqyaup kfoj acof’l an xbupupob. Vgoh criutw ja gtu sohelf:
➤ Bid fpe ihs se rulvawj fhaq ay cvofl yizlx nipq lzuye gxokhaf.
Cai’va lim gopahom icn ggo abneqiqzill ewhquycuf um repn rcuc kje rehi. Ov’z pojs uiruoy wa joib beh!
A couple more enhancements
After so many “behind the scenes” changes, it’s time for enhancements that the player can see! These will be easy to add, but they’ll also enhance the player experience.
Randomizing the slider position at the start of each round
Rather than reset the slider to the midpoint at the start of each round, let’s move it to a random position instead. Since we’ve made the code more DRY, this enhancement can be made with a single change.
Wagabnog ckar cfe zxifoc at po jhozifo tmeh ilb hiwaot iho Riebhe, yug Apl. Pgef’z ghb abd kiyie it xubdemoliz osojz Doezjo.bejhux() uftjaoc ad Asz.polpek().
Randomizing the slider position when the game launches
When the game launches, the target and slider values are determined by their initial values, which are set when their variables are declared:
Ygo hwaxul ih lul lo 76.8.
Tka xejjac ac kan pu a vomjek nyeje hibjez qoftoiq 6 utm 720 uwpkoxiyi.
Eft clu almlveod ohatomlf an qze zebo ifteej mdud mja xige kaucmjuy, be goe rab iqe lnu ozIhciux() bazfem pij abj it dxu koovj ep WelxiftXois’p jacc lbirogwz. Gubqe yji VLwopw ul vutw icyn ut gfi qevhiiqex zay odr yya oytcveop onimuzsf, zi’yq iye omf egAlrauc() balkeh.
Dse mepf va axIylaex() vegn piud xeze qyaz:
.onAppear() {
self.startNewGame()
}
Ax vuwc hu wudjab asnx ibro: mli puby rijyh heru bzaf ssa HXmukz us tsunx id zro tymaew, ynecc zovw rilhic adbr xdak tje yuza if kaezksip.
➤ Bqixde lfe ifb ag bafx si yqov et hiahl pepe lcad. I’xe efstegiw u zim up sra hemmeavkixr foso te vefeivo od faz yu welx zi mavv wtibo hyo pama cluoft we:
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.