2018年9月26日 星期三

Privacy Policy

CussDev built the Apps as an Ad Supported app. This SERVICE is provided by CussDev at no cost and is intended for use as is.

This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service.

If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.

The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at CussDev's Apps unless otherwise defined in this Privacy Policy.

Information Collection and Use

For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information. The information that I request will be retained on your device and is not collected by me in any way.

The app does use third party services that may collect information used to identify you.

Log Data

I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics.

Service Providers

I may employ third-party companies and individuals due to the following reasons:

To facilitate our Service;
To provide the Service on our behalf;
To perform Service-related services; or
To assist us in analyzing how our Service is used.
I want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.

Security

I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security.

Links to Other Sites

This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.

Children’s Privacy

These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions.

Changes to This Privacy Policy

I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.

Contact Us

If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me.

2018年1月11日 星期四

[遊戲] "暗棋王"的翻棋策略

Android 版本
iOS 版本

暗棋是一個深受台灣人喜愛的象棋遊戲,
除了實力外,運氣的好壞也會影響到輸贏,
暗棋的手遊不少,最受人攻擊的點就是有沒有作弊之類的,
以下就"暗棋王"APP開發過程中所考量的翻棋策略做小小的探討,
當然,也期待更多的高手先進多多指教,
讓"暗棋王"的程式演算法更加進步。

以下範例以電腦是紅棋,對手(玩家)是黑棋來講解...
1. "兵"旁邊翻棋
"兵"是所有棋子最弱的,幾乎所有棋子可以吃它,所以在它旁邊翻棋其實是很危險的,有極有的機會害它被對方吃掉,
然而,它也是最強的,因為只有它可以吃"將",
有些玩家會在"兵"旁邊直接翻棋,如果剛好翻到對方的"將",那就賺到了,如果沒翻到,大不了損失一隻"兵",影響不大,不過,已經死了幾隻"兵",這種策略就不適合,萬一"兵"全死光了,對方的"將"可能就會無敵

2. "帥"旁邊翻棋
 "帥 "是棋子裡最強的,所以在它旁邊翻棋,有機會可以一路吃下去,
然而,常常事與願違,偏偏旁邊翻出的是一隻 "卒 ",這也是暗棋王常常被說作弊的主因, "帥 "旁邊是 "卒 "的機率太高了,
讓我們從數學機率來解釋好了,
如果 "帥 "是位於棋盤4個角,它旁邊會有2顆棋子, "卒 "有5顆,所以 "帥 "旁邊有 "卒 "的機率是2*(5/31),棋盤有4個角,所以總機率是2*(5/31)*(4/32),約0.040,也就是4%,
如果 "帥 "是位於棋盤邊上,它旁邊會有3顆棋子, "卒 "有5顆,所以 "帥 "旁邊有 "卒 "的機率是3*(5/31),棋盤邊上的位置有16個,所以總機率是3*(5/31)*(16/32),約0.242,也就是24.2%,
如果 "帥 "是位於棋盤中間,它旁邊會有4顆棋子, "卒 "有5顆,所以 "帥 "旁邊有 "卒 "的機率是4*(5/31),棋盤中間的位置有12個,所以總機率是4*(5/31)*(12/32),約0.242,也就是24.2%,
將以上所有可能加總0.040+0.242+0.242 = 0.524,也就是52.4%,
換句話說, "帥 "旁邊是 "卒 "是機率約5成左右,機率很高,所以不建議在 "帥 "旁邊直接翻棋,除非 "卒 "已經全死光了^^,
以上的機率是用概估的方式,如有錯誤,歡迎更正

3. "炮"隔子翻棋
"炮"不能直接吃任何隔壁棋子,但如果隔一棋子,它可以吃任何一顆棋,而且距離不限,如下圖:
"炮"可以吃A,B,C三顆棋,紅方可以翻這三顆棋,如果是黑棋,下一輪就可以吃掉它們,
然而,如果翻出的剛好是"包","炮"反而會被吃掉,紅方不可一廂情願,
但如果如上圖中"炮"旁邊有一顆"俥",這時就可以肆無忌憚的翻ABC三顆棋,就算翻出"包",頂多同歸於盡

4. 防止被"包"吃
"包"隔子可吃任何棋,如果己方的棋要被"包"吃了,怎麼處理呢?
如果是如下圖的情形:

紅方可以翻ABC任何一顆棋,如果翻出"帥""仕""相""俥""傌"等棋子,就可以間接達到保護紅"仕"的作用,當然,如果黑方認為用"包"去換"仕"是值得的,那黑方還是會照吃。
如果是下圖的情形:

"包"往下走一步就可以吃紅"仕",紅方可以翻DEF三個位置的棋子,如果翻出"帥""仕""相""俥""傌"等棋子,就可以直接達到保護紅仕的作用,當然,紅方還是可以翻ABC位置的棋,如上段所提,可以間接保護紅"仕",選擇翻AC二處還有一個作用,如果是黑棋,比紅"仕"小的話,紅"仕"吃它就可以逃離"包"的威脅,翻到"將""士"也沒差,反正紅"仕"本來就快可了

5. "包"隔壁翻棋
"包"無法吃任何隔壁棋子,所以我們可以直接在"包"旁邊直接翻棋,反正翻出來的棋一定不會死,反之,如果翻出的棋可以吃掉"包",那就賺到了,因為"包"的威力很強

6. 保護性翻棋
如下圖左方,"仕"要被"將"吃了:

紅方可選擇翻ABC三個位置的棋,如果剛好翻到"兵",可達到間接保護"仕"的作用,因為"兵"有好幾顆,翻出機率大,有時可以做這樣的賭注,不過,還是有不小的可能會翻出其它紅棋,這樣便會讓"將"有連吃的機會。另外如果紅方"炮"還在,可選擇翻D處,這種翻法是最安全的,翻到"炮",可間接保護仕,翻到其它紅棋也不會被"將"吃掉,
如果是如上圖右方,"帥"要被"卒"吃了,紅方一樣可選擇翻GEF或H處的棋,不過,這種情形下間接保護的作用就不大了,因為不管翻到什麼棋,黑方一定會選擇了吃了"帥"再說,死一顆"卒"無所謂

2018年1月8日 星期一

[ 遊戲 ] 暗棋王 手遊APP

Android 版本
iOS 版本

“暗棋”,或者有人稱為”半棋”或”半盤”,是一個很受歡迎的棋盤遊戲
它很簡單且好玩,只要幾分鐘就可以玩完一局。
如果您想獲得勝利,您同時需要運氣和策略。
不管大人或小孩,都可以輕鬆享受這個遊戲。

本軟體的特色如下:
◎ 內建人工智慧(AI),您可以享受個人與電腦對打的樂趣
◎ 可以選擇不同的難度
◎ 提供個人戰績的統計資料
◎ 支援單人模式(和電腦玩)和雙人模式

暗棋的翻棋策略常常會影響勝負,
何時該翻?要翻那裡?
翻棋的結果又有點靠運氣,
這些變化都是暗棋特殊的樂趣。

雖然程式已多方考量,但畢竟人工智慧還是沒有人類聰明,
如果有需要改善的地方,也歡迎多多指教!

期待您一同加入暗棋玩家的行列…







2016年1月25日 星期一

[게임] 바둑 - 사활 문제 APP


Android 해제

iOS 해제

이 응용 프로그램은 사활 문제의 100 개 이상의 운동을 포함.
그들 모두는 무료입니다.
사활 문제는 이동 게임에 매우 중요하다.
게임을이기거나 할 수없는 경우 사실, 결정할 수있다.

모든 운동을 완료하기 위해 서둘러하지 마십시오.
당신이 다음 운동에 가기 전에 모든 가능성을 확인합니다.
이 응용 프로그램은 모든 운동을 몇 번 통과하는 경우는 경력이 속도를 증가하는 데 도움이 될 것입니다.
의 함께 진행을하자 ...






[ゲーム]囲碁 - 詰め碁演習APP


Androidのバージョン

iOSのバージョン

このアプリは、詰め碁の100以上の練習問題を含んでいます。
それらのすべてが無料です。
詰め碁演習はのため囲碁のゲームは非常に重要です。
実際に、それはあなたがゲームに勝つことができるかどうかを決定することができます。

すべての演習を完了するために急いでしないでください。
あなたは次の演習に進む前に、すべての可能性をご確認ください。
このアプリは、あなたがすべての演習を数回通過する場合は、勝率を高めるのに役立ちます。
ご一緒に進捗状況を取得してみましょう...





[ 游戏 ] 围棋 - 死活练习手游APP


Android 版本

iOS 版本

这个APP包括超过上百个死​​活练习题目,
所有题目都是免费就可以练习。

死活问题对于围棋来说很重要,
事实上,它可能会决定你是否能在下棋时获胜。

不要急着把所有练习一次做完,
在进到下一题之前,先把目前题目所有可能的变化都演练一次,
这对于提升你的棋力很有帮助。
让我们一起努力吧....




[ 遊戲 ] 圍棋 - 死活練習手遊APP


Android 版本

iOS 版本

這個APP包括超過上百個死活練習題目,
所有題目都是免費就可以練習。

死活問題對於圍棋來說很重要,
事實上,它可能會決定你是否能在下棋時獲勝。

不要急著把所有練習一次做完,
在進到下一題之前,先把目前題目所有可能的變化都演練一次,
這對於提升你的棋力很有幫助。
讓我們一起努力吧....





2015年11月17日 星期二

[Corona SDK] How to support multiple Ads networks - AdMob, iAds, Vungle

InHow to add advertisement in my APP - using AdMob V2, we learn how to add AdMob Ads.
For iAds and Vungle, we can use the similar way to add them.
We just consider how to support multiple networks here.
That it, we want to select AdMobiAds or Vungle dynamically, no matter it is for Fill Rate, eCPM or any other reasons.

Add plugin
First, we need add plug-in in build.settings:
settings =
{
 plugins =
    {
       ["plugin.google.play.services"] =
        {
            publisherId = "com.coronalabs"
        },
        ["CoronaProvider.ads.iads"] =
        {
            publisherId = "com.coronalabs",
            supportedPlatforms = { iphone=true, ["iphone-sim"]=true },
        },
        ["CoronaProvider.ads.vungle"] =
        {
            publisherId = "com.vungle",
        },      
    },
 android =
    {

        usesPermissions =
        {
            "android.permission.INTERNET",
            "android.permission.ACCESS_NETWORK_STATE",
        },
    },
}


Init the Ads
local adsProvider = {"adMob1","adMob2","vungle","iAds"} 
local ads = require( "ads" )
local adMobAdProvider = "admob"
local iAdsAdProvider = "iads"
local vungleAdProvider = "vungle"
local vungleAppId = "55e4xxxxxx"
local bannerAppID = "ca-app-pub-xxxxxx"
local interstitialAppID = "ca-app-pub-xxxxxx"
local appID = "com.gmail.MyCompany.App"
if ( isAndroid ) then
    bannerAppID = "ca-app-pub-xxxxx"
    interstitialAppID = "ca-app-xxxxxx"
    vungleAppId = "com.gmail.My_Company.App"
end
local lastAdsTypeShown = "none"
local currentAds = 1

local function adMobListener( event )
    local msg = event.response
    -- Quick debug message regarding the response from the library
    print( "Message from the adMob library: ", msg )
 if (event.type == "banner") then
     if ( event.isError ) then
         changeToNextAds()
     else
     end
 elseif(event.type == "interstitial") then
        if ( event.isError ) then
            changeToNextAds()
        elseif ( event.phase == "loaded" ) then
        elseif ( event.phase == "shown" ) then
        end
 end  
end

local function iAdsListener( event )
 local msg = event.response
 print("Message received from the iAds library: ", msg)
 if event.isError then
  changeToNextAds() 
 else
 end  
end

local function vungleListener( event )
   print("Message received from the vungleListener library: ", event.type)

   if ( event.type == "adStart" and event.isError ) then
     changeToNextAds()
   elseif ( event.type == "adEnd" ) then
      -- Ad was successfully shown and ended; hide the overlay so the app can resume.
   else
      print( "Received event", event.type )
   end
   return true
end

function adsInit()
 ads.init( adMobAdProvider, bannerAppID, adMobListener )
 if(isAndroid == false) then
   ads.init( iAdsAdProvider, appID, iAdsListener )    
 end
 ads.init( vungleAdProvider, vungleAppId, vungleListener )
end
We have to call ads.init() for AdMob,iAds and Vungle separately.
The parameters in their callback functions are different.
In above codes, we change to next Ads network when we get error.

Change Ads Network
We set new Ads network by function ads:setCurrentProvider().
local function changeToNextAds()
 currentAds = currentAds + 1
 if(currentAds > 3) then
  currentAds = 1
 end
 if(adsProvider[currentAds] == "iAds") then
  ads:setCurrentProvider(iAdsAdProvider)
 elseif(adsProvider[currentAds] == "vungle") then
  ads:setCurrentProvider(vungleAdProvider)
 else
  ads:setCurrentProvider(adMobAdProvider)
 end
end


Show Ads
function showAds(type,px,py)
 if(adsProvider[currentAds] == "adMob") then
  if(type == "banner") then
   ads.show( type, { x=px, y=py, appId=bannerAppID} )
   return true
  elseif(type == "interstitial") then    
   if(ads.isLoaded(type))then
    ads.show( type, { x=px, y=py, appId=interstitialAppID } )
    return true
   else
    return false
   end 
  end 
 elseif(adsProvider[currentAds] == "vungle") then
  if (type == "interstitial" and ads.isAdAvailable() ) then
      ads.show( "interstitial" )
      return true
  else
   return false
  end   
 elseif(adsProvider[currentAds] == "iAds") then
  ads.show( type, { x=px, y=py} )
  return true
 end
 return false  
end

function loadAds()
 if(adsProvider[currentAds] == "adMob") then
  ads.load( "interstitial", { appId=interstitialAppID} )
 end
end 
Vungle does not have Banner Ads and it will pre-load interstitial Ads itself.
iAds cannot pre-load interstitial Ads,
The codes shown above are very completed. 
You can just copy them directly and make it work.

[Corona SDK] 如何支援多個廣告系統 - AdMob, iAds, Vungle

如何加入廣告 - AdMob V2裡,
我們知道要如何加入AdMob Ads,
要加入iAdsVungle的話,作法很類似,
在這裡我們討論如果要同時支援的話該如何處理,
也就是說,我們可以動態選擇 AdMob, iAds, Vungle,
不管是因為Fill Rate, eCPM或者其它原因...

Add plugin
首先,我們需要在build.settings加入plug-in:
settings =
{
 plugins =
    {
       ["plugin.google.play.services"] =
        {
            publisherId = "com.coronalabs"
        },
        ["CoronaProvider.ads.iads"] =
        {
            publisherId = "com.coronalabs",
            supportedPlatforms = { iphone=true, ["iphone-sim"]=true },
        },
        ["CoronaProvider.ads.vungle"] =
        {
            publisherId = "com.vungle",
        },      
    },
 android =
    {

        usesPermissions =
        {
            "android.permission.INTERNET",
            "android.permission.ACCESS_NETWORK_STATE",
        },
    },
}
另外,Android需要增加網路存取的權限

Init the Ads
local adsProvider = {"adMob1","adMob2","vungle","iAds"} 
local ads = require( "ads" )
local adMobAdProvider = "admob"
local iAdsAdProvider = "iads"
local vungleAdProvider = "vungle"
local vungleAppId = "55e4xxxxxx"
local bannerAppID = "ca-app-pub-xxxxxx"
local interstitialAppID = "ca-app-pub-xxxxxx"
local appID = "com.gmail.MyCompany.App"
if ( isAndroid ) then
    bannerAppID = "ca-app-pub-xxxxx"
    interstitialAppID = "ca-app-xxxxxx"
    vungleAppId = "com.gmail.My_Company.App"
end
local lastAdsTypeShown = "none"
local currentAds = 1

local function adMobListener( event )
    local msg = event.response
    -- Quick debug message regarding the response from the library
    print( "Message from the adMob library: ", msg )
 if (event.type == "banner") then
     if ( event.isError ) then
         changeToNextAds()
     else
     end
 elseif(event.type == "interstitial") then
        if ( event.isError ) then
            changeToNextAds()
        elseif ( event.phase == "loaded" ) then
        elseif ( event.phase == "shown" ) then
        end
 end  
end

local function iAdsListener( event )
 local msg = event.response
 print("Message received from the iAds library: ", msg)
 if event.isError then
  changeToNextAds() 
 else
 end  
end

local function vungleListener( event )
   print("Message received from the vungleListener library: ", event.type)

   if ( event.type == "adStart" and event.isError ) then
     changeToNextAds()
   elseif ( event.type == "adEnd" ) then
      -- Ad was successfully shown and ended; hide the overlay so the app can resume.
   else
      print( "Received event", event.type )
   end
   return true
end

function adsInit()
 ads.init( adMobAdProvider, bannerAppID, adMobListener )
 if(isAndroid == false) then
   ads.init( iAdsAdProvider, appID, iAdsListener )    
 end
 ads.init( vungleAdProvider, vungleAppId, vungleListener )
end
我們需要針對AdMob,iAdsVungle各自呼叫ads.init()
因為callback回傳的參數不同,所以各自傳入不同callback function,
上面的程式裡,我們在callback function裡發現有錯誤時就換下一個Ads,
至於changeToNextAds()是什麼,看下一段

Change Ads Network
local function changeToNextAds()
 currentAds = currentAds + 1
 if(currentAds > 3) then
  currentAds = 1
 end
 if(adsProvider[currentAds] == "iAds") then
  ads:setCurrentProvider(iAdsAdProvider)
 elseif(adsProvider[currentAds] == "vungle") then
  ads:setCurrentProvider(vungleAdProvider)
 else
  ads:setCurrentProvider(adMobAdProvider)
 end
end
我們透過呼叫ads:setCurrentProvider()來設定新的Ads Network,

Show Ads
function showAds(type,px,py)
 if(adsProvider[currentAds] == "adMob") then
  if(type == "banner") then
   ads.show( type, { x=px, y=py, appId=bannerAppID} )
   return true
  elseif(type == "interstitial") then    
   if(ads.isLoaded(type))then
    ads.show( type, { x=px, y=py, appId=interstitialAppID } )
    return true
   else
    return false
   end 
  end 
 elseif(adsProvider[currentAds] == "vungle") then
  if (type == "interstitial" and ads.isAdAvailable() ) then
      ads.show( "interstitial" )
      return true
  else
   return false
  end   
 elseif(adsProvider[currentAds] == "iAds") then
  ads.show( type, { x=px, y=py} )
  return true
 end
 return false  
end

function loadAds()
 if(adsProvider[currentAds] == "adMob") then
  ads.load( "interstitial", { appId=interstitialAppID} )
 end
end 
Vungle沒有Banner Ads, 它自己會pre-load interstitial Ads,
iAds無法pre-load interstitial Ads,
上面的說明應該很完整,
基本上只要照抄就可以了

2015年11月16日 星期一

[Corona SDK] How to add Apple Game Center and Android Google Play Game Service

For Apple Game Center or Android Google Play Game Service, 
we just need to utilize the function of gameNetwork.
It is not difficulty to implement them.
To consider Apple and Google at the same time, 
let's check the codes first and see how them work:
local gameNetwork = require( "gameNetwork" )
local playerName
local playerId

local leaderboardsListId=
{
 ["HighestScore"]=
 {
  ["Description"] = "Highest Score",
  ["Identifier"] = "com.gmail.mycom.demo.Leaderboards.HighestScore"
 }
}

local achievementsListId = 
{
 ["FinishLevel1"]=
 {
  ["Description"] = "Finish Level 1",
  ["Identifier"] = "com.gmail.mycom.demo.Achievements.FinishLevel1"
 },
 ["FinishLevel2"]=
 {
  ["Description"] = "Finish Level 2",
  ["Identifier"] = "com.gmail.mycom.demo.Achievements.FinishLevel2"
 }
}

if(isAndroid) then
 leaderboardsListId=
 {
  ["HighestScore"]=
  {
   ["Description"] = "Highest Score",
   ["Identifier"] = "Cgkxxxxxxxx"
  }
 }

 achievementsListId = 
 {
  ["FinishLevel1"]=
  {
   ["Description"] = "Finish Level 1",
   ["Identifier"] = "Cgkxxxxxxx"
  },
  ["FinishLevel2"]=
  {
   ["Description"] = "Finish Level 2",
   ["Identifier"] = "Cgkxxxxxxxx"
  }
 }
end



function showLeaderboards( event )
 print("showLeaderboards()")
 if (playerId ~= nil) then
    if ( isAndroid ) then
       gameNetwork.show( "leaderboards" )
    else
       gameNetwork.show( "leaderboards", { leaderboard = {timeScope="AllTime"} } )
    end
 else
  gameNetworkSetup()
 end
    return true
end

local function postScoreSubmit( event )
   --whatever code you need following a score submission...
   return true
end

local function updateLeaderBoards(type, myScore)
 if (playerId == nil) then
  return
 end

 gameNetwork.request( "setHighScore",
 {
    localPlayerScore = { category=leaderboardsListId[type]["Identifier"], value=tonumber(myScore) },
    listener = postScoreSubmit
 } )
 print("gameNetwork.request( setHighScore) finished")
end

function showAchievements( event )
 print("showAchievements()")
 if (playerId ~= nil) then
    gameNetwork.show( "achievements" )
 else
  gameNetworkSetup()
 end
    return
end

local function achievementRequestCallback( event )
   
   return true
end

local function updateAchievement(type)
 if (playerId == nil) then
  return
 end
 gameNetwork.request( "unlockAchievement",
 {
    achievement = { identifier=achievementsListId[type]["Identifier"], percentComplete=100, showsCompletionBanner=true },
    listener = achievementRequestCallback
 } )
end

local function loadScoresCallback( event ) 
 if(event.data == nil) then
  print("event.data is nil")
  return
 end
 for i=1,25 do
  if(event.data[i] == nil) then
   break
  end
  print("event.data[",i,"].playerID:",event.data[i].playerID)
  print("event.data[",i,"].category:",event.data[i].category)
  if(event.data[i].playerID == playerId) then
   print("matched playID...........")
   break
  end
 end
end

local function loadLocalPlayerCallback( event ) 
 if(event.data == nil) then
  print("event.data is nil")
  return
 end

 playerId = event.data.playerID
 playerName = event.data.alias
 print("loadLocalPlayerCallback(),playerName:",playerName,",playerId:",playerId)
 if(playerId == nil) then
  return
 end
 myGameSettings.gameCerterHasEverLoggedIn = true
 saveData()   

 gameNetwork.request( "loadScores",
            {
                leaderboard =
                {
                    category = leaderboardsListId["HighestScore"]["Identifier"], 
                    playerScope = "Global",   -- Global, FriendsOnly
                    timeScope = "AllTime",    -- AllTime, Week, Today
                    --range = {1,5},
                    playerCentered = true,
                },
                listener = loadScoresCallback
            }) 
end

local function loadAchievementCallback( event )    
 if(event.data == nil) then
  print("event.data is nil")
  return
 end

 for i=1,#event.data do
  if(event.data[i] ~= nil) then
   print("event.data[",i,"].identifier:",event.data[i].identifier)
   print("event.data[",i,"].title:",event.data[i].title)
   print("event.data[",i,"].isCompleted:",event.data[i].isCompleted)
  end
 end 
end

local function gameNetworkLoginCallback( event )
 if(isAndroid==false) then
  if(event.data) then
   print("User has logged into iOS Game Center")
  else
   print("User has NOT logged into iOS Game Center")
   return 
  end
 end
 gameNetwork.request( "loadLocalPlayer", { listener=loadLocalPlayerCallback } )
 gameNetwork.request( "loadAchievements", { listener=loadAchievementCallback } )
 return true
end

local function gpgsInitCallback( event )
 if(event.data) then
  print("User has logged into Google App Play Center")
     gameNetwork.request( "login", { userInitiated=true, listener=gameNetworkLoginCallback } )
 else
  print("User has NOT logged into Google App Play Center")
 end
end

function gameNetworkSetup()
 if ( isAndroid ) then
  myGameSettings.gameCerterHasEverLoggedIn = false
  gameNetwork.init( "google", gpgsInitCallback )
 else
  gameNetwork.init( "gamecenter", gameNetworkLoginCallback )
 end
end
}
Init
In general, we can call gameNetworkSetup() in main.lua
For Android, we need to check if we have logged in successfully in gpgsInitCallback().
For iOS, we need to check if we have logged in successfully in gameNetworkLoginCallback().
The code gameNetwork.request( "loadLocalPlayer", { listener=loadLocalPlayerCallback } ) is to confirm the user who logs in.
You can go without it.
The code gameNetwork.request( "loadAchievements", { listener=loadAchievementCallback } ) is to recover the users's records.
For example, users may have ever installed this APP, remove this APP, and re-install it again.
If you think that you don't need to recover the records for them, you can go with it.

Show leaderboard
To show the leaderboard, we just need to call function showLeaderboards().
In general, we do it when user press some button

Show achievement
To show the leaderboard, we just need to call function showAchievements().
In general, we do it when user press some button.

Update leaderboard

To update leaderboard, we just need to call function updateLeaderBoards().
In general, wwe do it when user finish the game.

Update achievements 

To update leaderboard, we just need to call function updateAchievement().
In general, wwe do it when user finish some round.


Below are what differ between Apple Game Center and Android Google Play Game Service:
Apple Game Center
The data in leaderboardsListId{} and achievementsListId{}:
e.g. "com.gmail.mycom.demo.Leaderboards.HighestScore".
They are user defined, as long as they are  not duplicated.

Google Play Game Service
The data in leaderboardsListId{} and achievementsListId{}:
e.g. "Cgkxxxxxxxx".
They are defined by play store publish when you finished the setting in game center.
For Google Play Game Service, we also need to add the googlePlayGamesAppId in build.settings:
android =
    {
        usesPermissions =
        {
            "android.permission.INTERNET",
            "android.permission.ACCESS_NETWORK_STATE",
            "com.android.vending.BILLING",
        },
        googlePlayGamesAppId = "123456789012" 
    },
usesPermissions are not concerned for game service,
googlePlayGamesAppId is what you get when you link the APP in play store publish game center.

[Corona SDK] 如何加入Apple Game Center 和 Android Google Play Game Service

不管是AppleGame Center,還是AndroidGoogle Play Game Service
基本上都是利用gameNetwork的功能即可,
實作不難,考慮同時支援AppleGoogle
先將程式碼貼上再來講解:
local gameNetwork = require( "gameNetwork" )
local playerName
local playerId

local leaderboardsListId=
{
 ["HighestScore"]=
 {
  ["Description"] = "Highest Score",
  ["Identifier"] = "com.gmail.mycom.demo.Leaderboards.HighestScore"
 }
}

local achievementsListId = 
{
 ["FinishLevel1"]=
 {
  ["Description"] = "Finish Level 1",
  ["Identifier"] = "com.gmail.mycom.demo.Achievements.FinishLevel1"
 },
 ["FinishLevel2"]=
 {
  ["Description"] = "Finish Level 2",
  ["Identifier"] = "com.gmail.mycom.demo.Achievements.FinishLevel2"
 }
}

if(isAndroid) then
 leaderboardsListId=
 {
  ["HighestScore"]=
  {
   ["Description"] = "Highest Score",
   ["Identifier"] = "Cgkxxxxxxxx"
  }
 }

 achievementsListId = 
 {
  ["FinishLevel1"]=
  {
   ["Description"] = "Finish Level 1",
   ["Identifier"] = "Cgkxxxxxxx"
  },
  ["FinishLevel2"]=
  {
   ["Description"] = "Finish Level 2",
   ["Identifier"] = "Cgkxxxxxxxx"
  }
 }
end



function showLeaderboards( event )
 print("showLeaderboards()")
 if (playerId ~= nil) then
    if ( isAndroid ) then
       gameNetwork.show( "leaderboards" )
    else
       gameNetwork.show( "leaderboards", { leaderboard = {timeScope="AllTime"} } )
    end
 else
  gameNetworkSetup()
 end
    return true
end

local function postScoreSubmit( event )
   --whatever code you need following a score submission...
   return true
end

local function updateLeaderBoards(type, myScore)
 if (playerId == nil) then
  return
 end

 gameNetwork.request( "setHighScore",
 {
    localPlayerScore = { category=leaderboardsListId[type]["Identifier"], value=tonumber(myScore) },
    listener = postScoreSubmit
 } )
 print("gameNetwork.request( setHighScore) finished")
end

function showAchievements( event )
 print("showAchievements()")
 if (playerId ~= nil) then
    gameNetwork.show( "achievements" )
 else
  gameNetworkSetup()
 end
    return
end

local function achievementRequestCallback( event )
   
   return true
end

local function updateAchievement(type)
 if (playerId == nil) then
  return
 end
 gameNetwork.request( "unlockAchievement",
 {
    achievement = { identifier=achievementsListId[type]["Identifier"], percentComplete=100, showsCompletionBanner=true },
    listener = achievementRequestCallback
 } )
end

local function loadScoresCallback( event ) 
 if(event.data == nil) then
  print("event.data is nil")
  return
 end
 for i=1,25 do
  if(event.data[i] == nil) then
   break
  end
  print("event.data[",i,"].playerID:",event.data[i].playerID)
  print("event.data[",i,"].category:",event.data[i].category)
  if(event.data[i].playerID == playerId) then
   print("matched playID...........")
   break
  end
 end
end

local function loadLocalPlayerCallback( event ) 
 if(event.data == nil) then
  print("event.data is nil")
  return
 end

 playerId = event.data.playerID
 playerName = event.data.alias
 print("loadLocalPlayerCallback(),playerName:",playerName,",playerId:",playerId)
 if(playerId == nil) then
  return
 end
 myGameSettings.gameCerterHasEverLoggedIn = true
 saveData()   

 gameNetwork.request( "loadScores",
            {
                leaderboard =
                {
                    category = leaderboardsListId["HighestScore"]["Identifier"], 
                    playerScope = "Global",   -- Global, FriendsOnly
                    timeScope = "AllTime",    -- AllTime, Week, Today
                    --range = {1,5},
                    playerCentered = true,
                },
                listener = loadScoresCallback
            }) 
end

local function loadAchievementCallback( event )    
 if(event.data == nil) then
  print("event.data is nil")
  return
 end

 for i=1,#event.data do
  if(event.data[i] ~= nil) then
   print("event.data[",i,"].identifier:",event.data[i].identifier)
   print("event.data[",i,"].title:",event.data[i].title)
   print("event.data[",i,"].isCompleted:",event.data[i].isCompleted)
  end
 end 
end

local function gameNetworkLoginCallback( event )
 if(isAndroid==false) then
  if(event.data) then
   print("User has logged into iOS Game Center")
  else
   print("User has NOT logged into iOS Game Center")
   return 
  end
 end
 gameNetwork.request( "loadLocalPlayer", { listener=loadLocalPlayerCallback } )
 gameNetwork.request( "loadAchievements", { listener=loadAchievementCallback } )
 return true
end

local function gpgsInitCallback( event )
 if(event.data) then
  print("User has logged into Google App Play Center")
     gameNetwork.request( "login", { userInitiated=true, listener=gameNetworkLoginCallback } )
 else
  print("User has NOT logged into Google App Play Center")
 end
end

function gameNetworkSetup()
 if ( isAndroid ) then
  myGameSettings.gameCerterHasEverLoggedIn = false
  gameNetwork.init( "google", gpgsInitCallback )
 else
  gameNetwork.init( "gamecenter", gameNetworkLoginCallback )
 end
end
}
初始化
一般來說,我們可以在main.lua裡呼叫gameNetworkSetup(),
對於Android系統,在gpgsInitCallback()裡可以確認是否登錄成功
對於iOS系統,在gameNetworkLoginCallback()裡可以確認是否登錄成功,
至於後面的gameNetwork.request( "loadLocalPlayer", { listener=loadLocalPlayerCallback } )
是用來確認目前登錄遊戲中心的使用者,有沒有呼叫它都可以,
gameNetwork.request( "loadAchievements", { listener=loadAchievementCallback } )則是用來回復使用者的紀錄, 例如使用曾經安裝過這個APP,移除它後又重新下載, 如果不想幫使用者回復紀錄,不執行也可以

顯示排行榜(leaderboard)
想要顯示排行榜,只要呼叫showLeaderboards()即可, 通常這個動作是在使用者按下某個按鈕時

顯示任務(achievement)
想要顯示排行榜,只要呼叫showAchievements()即可, 通常這個動作是在使用者按下某個按鈕時

更新排行榜 想要更新排行榜,只要呼叫updateLeaderBoards()即可, 通常這個動作是在使用者完成遊戲時

更新任務 想要更新排行榜,只要呼叫updateAchievement()即可, 通常這個動作是在使用者完成某個關卡時


以下是AppleGame Center,和AndroidGoogle Play Game Service稍有不同的地方
Apple Game Center
leaderboardsListId{}achievementsListId{}裡的資料, 例如"com.gmail.mycom.demo.Leaderboards.HighestScore", 這是使用者自己決定的編碼,然後在iTunes Connect裡設定即可, 不重覆就行

Google Play Game Service
leaderboardsListId{}和achievementsListId{}裡的資料,
例如"Cgkxxxxxxxx", 這是使用者在play store publish的遊戲中心設定後系統給的編碼, 另外,對於Google Play Game Service, 我們還需要在build.settings增加:
android =
    {
        usesPermissions =
        {
            "android.permission.INTERNET",
            "android.permission.ACCESS_NETWORK_STATE",
            "com.android.vending.BILLING",
        },
        googlePlayGamesAppId = "123456789012" 
    },
usesPermissions無所謂,
googlePlayGamesAppId是在play store publish的遊戲中心連結應用程式後系統給的編碼

[ 遊戲 ] 空戰 - 火力全開


https://www.facebook.com/Air-Fight-Infinite-Power-189988404668625/

你有無盡的火力來消滅敵人戰機。
小心接踵而來的敵機和巨量的子彈攻擊,
當你有危險時,不要遲疑,趕快發射炸彈來保護你自己。

如果你喜歡射擊遊戲,那你一定會喜歡這個APP。

[ Game ] Air Fight - Infinite Power



https://www.facebook.com/Air-Fight-Infinite-Power-189988404668625/


You have infinite power to clear the enemies.

Be careful of consecutive enemy fighters and massive bullets.
Don't hesitate to shoot the bomb when you are in danger.

If you like shooting game, you will love this app.

[ 遊戲 ] 桌上撞盤


https://www.facebook.com/Table-Discs-1477277249248292


這是一個有趣的新型態遊戲。

第一眼看起有點像撞球中的開侖,
但是它其實更為簡單且好玩。

你不需要有什麼開超的撞球技巧,
只要將白盤射出,讓它撞到紅盤即可。

當然,這中間一定會有許多的障礙來阻撓你...

[ Game ] Table Discs


https://www.facebook.com/Table-Discs-1477277249248292


This is an interesting game in new type.
It look like Carom at first sight.
However, it is easier and has more fun.

You don't need to have good skills for billiards game.
Just shoot the white disc and let it collide with red disc.

Of course, there will have some obstacles to interfere with your movement.

[ 遊戲 ] 推推樂



https://www.facebook.com/pages/Push-Discs/1632278527059052

想要運動您的手指嗎?
"推推樂"是一個簡單好玩但又充滿挑戰性的遊戲。
它支援Google遊戲中心。
快邀請您的朋友一同來挑戰它。

[ Game ] Push Discs


https://www.facebook.com/pages/Push-Discs/1632278527059052

Want to exercise your finger?
"Push Discs" is a simple but full of challenging game.
It supports Google Play Game Services.
Invite your friends to challenge it together.

[ 遊戲 ] 單字矩陣無限玩



https://www.facebook.com/Infinite-Word-Matrix-3-letters-874519139268720/

你認識很多3個字母的單字嗎?

這太簡單了吧?要拼出3個字的單字實在沒什麼難度,
不過,其實這會比你想像中的困難,
因為3個字的單字比你想像中的少,
再者,有些字又很相似,
例如 "ria","rib","rid","rif","rig","rim","rip"....
我們可能會認為 "rie", "rit"等等也都是有意義的單字,
但...其實不然。

這個APP不只是個好玩的拼字遊戲,也是一個幫助你練習和覆習拼字的工具。

[ Game ] Infinite Word Matrix


https://www.facebook.com/Infinite-Word-Matrix-3-letters-874519139268720/


How many words in 3 letters do you know?

You may think that it is easy to spell out the words in 3 letters.
However, the number of words in 3 letters is less than you expect.
Furthermore, many words are similar except for one letter difference.
For example, "ria","rib","rid","rif","rig","rim","rip"....
We may think that there should have words such as "rie", "rit".
But...you get the wrong answers.


This APP is not only a game for fun, it is also a tester, reminder for you to improve your spell skill.

[ 遊戲 ] 撲克陣列



https://www.facebook.com/Infinite-Poker-Matrix-1591171817815298

一個玩不停的撲克遊戲。
你只要點選任何5張牌即可。

牌面越大,你得到的金幣就會越多。
不過,當你在選擇牌時,你必須同時考慮下一次的5張牌應該是什麼。
這是一個完全免費的遊戲。