Class: Mjai::Manue::HoraPointsEstimate

Inherits:
Object
  • Object
show all
Defined in:
lib/mjai/manue/hora_points_estimate.rb

Defined Under Namespace

Classes: HoraCombination, ProbablisticFan

Constant Summary collapse

TSUMO_HORA_PROB =

TODO Calculate with statistics.

0.5
SUPPORTED_YAKUS =

TODO Add ippatsu, uradora

[:reach, :tanyaochu, :pinfu, :iso, :fanpai, :dora, :akadora]
DORA_YAKUS =
[:dora, :akadora, :uradora]
FU_MAP =

key: [menzen, tsumo, pinfu]

{
    [false, false, false] => 30,
    [false, false, true] => 30,
    [false, true, false] => 30,
    [false, true, true] => 30,
    [true, false, false] => 40,
    [true, false, true] => 30,
    [true, true, false] => 30,
    [true, true, true] => 20,
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params) ⇒ HoraPointsEstimate

Returns a new instance of HoraPointsEstimate.



246
247
248
249
250
251
252
253
254
255
256
# File 'lib/mjai/manue/hora_points_estimate.rb', line 246

def initialize(params)
  @shanten_analysis = params[:shanten_analysis]
  @furos = params[:furos]
  @context = params[:context]
  
  @expanded_combinations = get_expanded_combinations()
  @used_combinations = get_used_combinations(@expanded_combinations)
  @hora_combinations = get_hora_combinations(@used_combinations)
  @yaku_pfans = get_yaku_pfans(@hora_combinations)
  @average_points = get_average_points(@yaku_pfans)
end

Instance Attribute Details

#average_pointsObject (readonly)

Returns the value of attribute average_points.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def average_points
  @average_points
end

#contextObject (readonly)

Returns the value of attribute context.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def context
  @context
end

#expanded_combinationsObject (readonly)

Returns the value of attribute expanded_combinations.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def expanded_combinations
  @expanded_combinations
end

#furosObject (readonly)

Returns the value of attribute furos.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def furos
  @furos
end

#hora_combinationsObject (readonly)

Returns the value of attribute hora_combinations.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def hora_combinations
  @hora_combinations
end

#shanten_analysisObject (readonly)

Returns the value of attribute shanten_analysis.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def shanten_analysis
  @shanten_analysis
end

#used_combinationsObject (readonly)

Returns the value of attribute used_combinations.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def used_combinations
  @used_combinations
end

#yaku_pfansObject (readonly)

Returns the value of attribute yaku_pfans.



258
259
260
# File 'lib/mjai/manue/hora_points_estimate.rb', line 258

def yaku_pfans
  @yaku_pfans
end

Class Method Details

.complete_candidates(mentsu) ⇒ Object



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/mjai/manue/hora_points_estimate.rb', line 438

def self.complete_candidates(mentsu)
  if [:shuntsu, :kotsu, :kantsu].include?(mentsu.type)
    return [[mentsu, 1.0]]
  end
  rcands = []
  case mentsu.type
    when :ryanmen, :penta
      rcands += [[:shuntsu, [-1, 0, 1]], [:shuntsu, [0, 1, 2]]]
    when :kanta
      rcands += [[:shuntsu, [0, 1, 2]]]
    when :toitsu
      rcands += [[:kotsu, [0, 0, 0]]]
    when :single
      rcands += [
          [:shuntsu, [-2, -1, 0]],
          [:shuntsu, [-1, 0, 1]],
          [:shuntsu, [0, 1, 2]],
          [:kotsu, [0, 0, 0]]
      ]
    else
      raise("should not happen: %p" % mentsu.type)
  end
  cands = []
  first_pai = mentsu.pais[0]
  for type, rnums in rcands
    in_range = rnums.all?() do |rn|
      (rn == 0 || first_pai.type != "t") && (1..9).include?(first_pai.number + rn)
    end
    if in_range
      cands.push(Mentsu.new({
          :type => type,
          :pais => rnums.map(){ |rn| Pai.new(first_pai.type, first_pai.number + rn) },
      }))
    end
  end
  return cands.map(){ |m| [m, 1.0 / cands.size] }
end

.janto_candidates(mentsu) ⇒ Object



476
477
478
479
480
481
# File 'lib/mjai/manue/hora_points_estimate.rb', line 476

def self.janto_candidates(mentsu)
  pai_cands = mentsu.pais.uniq()
  return pai_cands.map() do |pai|
    [Mentsu.new({:type => :toitsu, :pais => [pai, pai]}), 1.0 / pai_cands.size]
  end
end

Instance Method Details

#dumpObject



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/mjai/manue/hora_points_estimate.rb', line 406

def dump()
  p [:shanten, self.shanten_analysis.shanten]
  p :orig
  for combi in self.shanten_analysis.combinations
    pp combi
  end
  p :detailed
  for combi in self.shanten_analysis.detailed_combinations
    pp combi
  end
  p :expanded
  for combi in self.expanded_combinations
    pp combi
  end
  p [:used, self.used_combinations.size]
  for combi in self.used_combinations
    pp combi
  end
  p [:hora, self.hora_combinations.size]
  for hcombi in self.hora_combinations
    p [:current_janto, hcombi.used_combination.janto.pais.join(" ")]
    for mentsu in hcombi.used_combination.mentsus
      p [:current_mentsu, mentsu.pais.join(" ")]
    end
    pp hcombi
  end
  for yaku, pfan in self.yaku_pfans
    p [yaku, pfan.probs.reject(){ |k, v| k == 0 }.sort()] if pfan.probs[0] < 0.999
  end
  p [:avg_pts, self.average_points]
end

#get_average_points(yaku_pfans) ⇒ Object



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/mjai/manue/hora_points_estimate.rb', line 350

def get_average_points(yaku_pfans)
  
  normal_pfan = yaku_pfans.select(){ |yk, pf| !DORA_YAKUS.include?(yk) }.
      map(){ |yk, pf| pf }.
      inject(ProbablisticFan.new(0), :+)
  dora_pfan = yaku_pfans.select(){ |yk, pf| DORA_YAKUS.include?(yk) }.
      map(){ |yk, pf| pf }.
      inject(ProbablisticFan.new(0), :+)
  probs = Hash.new(0.0)
  for nf, np in normal_pfan.probs
    for df, fp in dora_pfan.probs
      probs[nf == 0 ? 0 : nf + df] += np * fp
    end
  end
  
  pinfu_prob = yaku_pfans[:pinfu].probs[1]
  is_menzen = @furos.empty?
  result = 0.0
  for is_tsumo in [false, true]
    for is_pinfu in [false, true]
      base_prob =
          (is_tsumo ? TSUMO_HORA_PROB : 1.0 - TSUMO_HORA_PROB) *
          (is_pinfu ? pinfu_prob : 1.0 - pinfu_prob)
      next if base_prob == 0.0
      fu = FU_MAP[[is_menzen, is_tsumo, is_pinfu]]
      for fan, fan_prob in probs
        fan += 1 if is_menzen && is_tsumo
        if fan > 0
          datum = Hora::PointsDatum.new(fu, fan, @context.oya, is_tsumo ? :tsumo : :ron)
          #p [is_tsumo, is_pinfu, base_prob, fan, fan_prob, fu, datum.points]
          result += datum.points * fan_prob * base_prob
        end
      end
    end
  end
  return result
  
end

#get_expanded_combinationsObject



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
# File 'lib/mjai/manue/hora_points_estimate.rb', line 275

def get_expanded_combinations()
  furo_mentsus = @furos.map(){ |f| f.to_mentsu() }
  result = []
  for combination in @shanten_analysis.detailed_combinations
    #p combination
    if combination.janto
      result.push(ShantenAnalysis::DetailedCombination.new(
          combination.janto, combination.mentsus + furo_mentsus))
    else
      num_groups = combination.mentsus.select(){ |m| m.pais.size >= 2 }.size
      combination.mentsus.each_with_index() do |mentsu, i|
        if mentsu.pais.size == 1
          maybe_janto = true
        elsif [:ryanmen, :kanchan, :penta].include?(mentsu.type) && num_groups >= 5
          maybe_janto = true
        else
          maybe_janto = false
        end
        if maybe_janto
          remains = combination.mentsus.dup()
          remains.delete_at(i)
          result.push(ShantenAnalysis::DetailedCombination.new(
              mentsu, remains + furo_mentsus))
        end
      end
    end
  end
  return result
end

#get_hora_combinations(used_combinations) ⇒ Object



333
334
335
# File 'lib/mjai/manue/hora_points_estimate.rb', line 333

def get_hora_combinations(used_combinations)
  return used_combinations.map(){ |c| HoraCombination.new(c, self) }
end

#get_used_combinations(expanded_combinations) ⇒ Object



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/mjai/manue/hora_points_estimate.rb', line 305

def get_used_combinations(expanded_combinations)
  result = Set.new()
  for combination in expanded_combinations
    completes = combination.mentsus.select(){ |m| m.pais.size >= 3 }.sort()
    tatsus = combination.mentsus.select(){ |m| m.pais.size == 2 }.sort()
    singles = combination.mentsus.select(){ |m| m.pais.size == 1 }.sort()
    #pp [:used_exp_combi, combination]
    #p [:completes, completes.size]
    mentsu_combinations = []
    if completes.size >= 4
      mentsu_combinations.push(completes)
    elsif completes.size + tatsus.size >= 4
      tatsus.combination(4 - completes.size) do |t_tatsus|
        mentsu_combinations.push(completes + t_tatsus)
      end
    else
      singles.combination(4 - completes.size - tatsus.size) do |t_singles|
        mentsu_combinations.push(completes + tatsus + t_singles)
      end
    end
    #p [:mentsu_combinations, mentsu_combinations]
    for mentsus in mentsu_combinations
      result.add(ShantenAnalysis::DetailedCombination.new(combination.janto, mentsus))
    end
  end
  return result
end

#get_yaku_pfan(yaku, hora_combinations) ⇒ Object



345
346
347
348
# File 'lib/mjai/manue/hora_points_estimate.rb', line 345

def get_yaku_pfan(yaku, hora_combinations)
  pfans = hora_combinations.map(){ |hc| hc.yaku_pfan(yaku) }
  return ProbablisticFan.prob_average(pfans)
end

#get_yaku_pfans(hora_combinations) ⇒ Object



337
338
339
340
341
342
343
# File 'lib/mjai/manue/hora_points_estimate.rb', line 337

def get_yaku_pfans(hora_combinations)
  result = {}
  for yaku in SUPPORTED_YAKUS
    result[yaku] = get_yaku_pfan(yaku, hora_combinations)
  end
  return result
end

#yaku_debug_strObject



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/mjai/manue/hora_points_estimate.rb', line 389

def yaku_debug_str
  [:reach, :tanyaochu, :pinfu, :fanpai, :dora, :akadora]
  short_names = [
      [:tanyaochu, "ty"],
      [:pinfu, "pf"],
      [:iso, "is"],
      [:fanpai, "fp"],
      [:dora, "dr"],
      [:akadora, "ad"],
  ]
  strs = short_names.map() do |yaku, short_name|
    exp_fan = @yaku_pfans[yaku].expected
    exp_fan < 0.001 ? nil : "%s=%.2f" % [short_name, exp_fan]
  end
  return strs.select(){ |s| s }.join(" ")
end